From e6340f2c3c8af662990198b58e64a0de46e609ac Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Fri, 4 Jul 2025 17:55:09 +0800 Subject: [PATCH 1/9] chore: bump version to 1.10.5 for RC --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 48cc9abe3..1d04e7743 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "esp-idf-extension", "displayName": "ESP-IDF", "description": "Develop and debug applications for Espressif chips with ESP-IDF", - "version": "1.10.3", + "version": "1.10.5", "license": "Apache-2.0", "publisher": "espressif", "icon": "media/espressif_icon.png", From 0edf5d3b273d2f52837f73baa60f9f0651a4ee7c Mon Sep 17 00:00:00 2001 From: "Brian A. Ignacio" Date: Thu, 24 Jul 2025 18:03:11 +0800 Subject: [PATCH 2/9] Pre-release of v2.0.0 (#1627) * draft eim integration * update init extension auto config * rm global state idf setups * do not show invalid extension when showOnboardingOnInit is false * rm duplicate code * update welcome show * fix new projext examples esp idf loading * update default esp_ide json file * add eim verify idf setup functions * update to eim_idf json file name * save settings as env variables * rm settings use customExtraVars instead * fix save docker settings * check empty customExtraVars PATH * fix getConfigurationSettings * rm old setup * fix lint * rm setup webpack update python in project test * use customextravars first * add selected workspace global config * use process env if customextra vars not available * default tools path * test doctor command * write customExtraVars from process env if available * fix workspace arg * update menuconfig test rm doc cmd * add PYTHON env var * log doctor command output clean project first * add missing timeout * test debug vscode * add PYTHON env var * add env PYTHON save settings * fallback on global state setups * escape activation script path if spaces * move IDF_PATH to workspace state, move frameworks settings to customExtraVars * update rm global setups * use env vars if idfSetups not defined, rm redudant code downloadManager and tests * read esp idf setups from esp_idf.json * await getConfigurationAccess * add logger init * fix IDF_TOOLS_PATH not defined if default * add PYTHON to env variables * change checkIdfSetup to isIdfSetupValid rm IEspIdfTool fields * remove env systemDriver use C in Windows default eim_idf json path * log activation script output * change to error logs * update activationScript split regex * fix IDF_PYTHON_ENV_PATH to envDict * loadIdfSetup save idf.gitPath * add path to prepareEnv * fix idf size ui * add idf setups in doctor command * add validation for eim load env setup * idfSetupToUse add validation * fix load idf.currentSetup * rm idf.espIdfPath from docs rm duplicate idfSetups use old idf.espIdfPath if no idf.currentSetup * add download extract install run eim from extension * fx linux exe call * use env shell for activation script * allow pick mirror eim download, add l10n for msg * fix errors * fix env vars in clang validate * use latest vscode-extension-tester * use eimPath field in eim_idf json to open existing executable if exists * rm idf setup validation, only in doctor cmd * fix checkPyVenv object * rm idf.toolsPath reference * use idf toolsPath old setting to find esp_idf json file * rm use of ESP_IDF_VERSION in status bar * add missing v for idf version * update walkthrough install step * add idf.eimExecutableArgs for eim gui or cli * use & intead of launch process * make sure that eim_unified_release is not cached, rm quote * close notifications befor doctor cmd * mv dismiss to before * fix lint * fix ts issues * use default tools path instead of temp for eim download * add default EIM locations to match Espressif IDE and use EIM_PATH * test json reporter no use of docker * add expected test env vars * add results in console too, rm actions idf fix doctor test * replace user path in doctor test * test using e2e ui reports * test extester mocha options * update options as object * update reporter option * try to parse json from console * fix console output parsing * update console output parse error handle * clean json step * update test log to test locally * rm text after closing json * update end regex * update parsing with test data * fail on test error * update bulma packages and use new syntax --- .github/actions/idf/Dockerfile | 44 - .github/actions/idf/action.yml | 19 - .github/actions/idf/entrypoint.sh | 27 - .github/actions/idf/ui-entrypoint.sh | 22 - .github/workflows/ci.yml | 49 +- .github/workflows/ui-test.yml | 87 +- .gitignore | 2 +- README.md | 40 +- README_CN.md | 10 +- .../additionalfeatures/docker-container.rst | 2 - .../additionalfeatures/multiple-projects.rst | 50 +- .../en/additionalfeatures/web-extension.rst | 3 - docs_espressif/en/commands.rst | 16 +- docs_espressif/en/configureproject.rst | 40 +- docs_espressif/en/installation.rst | 45 +- docs_espressif/en/settings.rst | 33 +- .../additionalfeatures/docker-container.rst | 2 - .../additionalfeatures/multiple-projects.rst | 6 +- .../additionalfeatures/web-extension.rst | 3 - docs_espressif/zh_CN/commands.rst | 12 +- docs_espressif/zh_CN/configureproject.rst | 6 +- docs_espressif/zh_CN/settings.rst | 33 +- export.bat | 7 - l10n/bundle.l10n.es.json | 11 +- l10n/bundle.l10n.pt.json | 11 +- l10n/bundle.l10n.ru.json | 10 +- l10n/bundle.l10n.zh-CN.json | 11 +- media/walkthrough/express-setup.png | Bin 15009 -> 0 bytes media/walkthrough/idf-version.png | Bin 10843 -> 0 bytes media/walkthrough/install-btn.png | Bin 4927 -> 0 bytes media/walkthrough/python-selection.png | Bin 14441 -> 0 bytes package.json | 121 +- package.nls.es.json | 14 +- package.nls.json | 14 +- package.nls.pt.json | 18 +- package.nls.ru.json | 15 +- package.nls.zh-CN.json | 14 +- src/build/buildTask.ts | 29 +- src/checkExtensionSettings.ts | 112 - src/clang/index.ts | 5 +- src/cmdTreeView/cmdStore.ts | 14 +- src/common/abstractCloning.ts | 41 +- src/common/prepareEnv.ts | 254 ++ src/common/store.ts | 23 - src/component-manager/utils.ts | 22 +- src/config.ts | 15 +- src/coverage/configureProject.ts | 26 +- src/coverage/gcdaPaths.ts | 5 +- src/customTasks/customTaskProvider.ts | 4 +- src/downloadManager.ts | 564 --- src/efuse/index.ts | 9 +- src/eim/downloadInstall.ts | 466 +++ src/eim/getExistingSetups.ts | 133 + src/eim/loadIdfSetup.ts | 179 + src/eim/loadSettings.ts | 100 + src/eim/migrationTool.ts | 211 ++ src/{views/setup => eim}/types.ts | 67 +- src/eim/verifySetup.ts | 191 + src/espAdf/espAdfDownload.ts | 2 +- src/espBom/index.ts | 8 +- src/espHomekit/espHomekitDownload.ts | 2 +- src/espIdf/core-dump/esp-core-dump-py-tool.ts | 11 +- src/espIdf/debugAdapter/checkPyReqs.ts | 52 - .../debugAdapter/debugAdapterManager.ts | 347 -- src/espIdf/debugAdapter/verifyApp.ts | 10 +- src/espIdf/documentation/getDocsVersion.ts | 46 +- src/espIdf/documentation/getSearchResults.ts | 8 +- src/espIdf/menuconfig/confServerProcess.ts | 25 +- src/espIdf/menuconfig/saveDefConfig.ts | 12 +- src/espIdf/monitor/checkWebsocketClient.ts | 7 +- src/espIdf/monitor/command.ts | 9 +- src/espIdf/monitor/index.ts | 7 +- src/espIdf/nvs/partitionTable/panel.ts | 12 +- src/espIdf/openOcd/boardConfiguration.ts | 20 +- src/espIdf/openOcd/openOcdManager.ts | 6 +- .../partition-table/partitionFlasher.ts | 15 +- src/espIdf/partition-table/partitionReader.ts | 9 +- src/espIdf/partition-table/tree.ts | 20 +- src/espIdf/reconfigure/task.ts | 11 +- src/espIdf/serial/serialPort.ts | 13 +- src/espIdf/setTarget/DevkitsCommand.ts | 29 +- src/espIdf/setTarget/getTargets.ts | 5 +- src/espIdf/setTarget/setTargetInIdf.ts | 9 +- src/espIdf/size/idfSize.ts | 23 +- src/espIdf/size/idfSizeTask.ts | 17 +- src/espIdf/tracing/gdbHeapTraceManager.ts | 29 +- .../tools/abstractTracingToolManager.ts | 11 +- .../tools/xtensa/abstractXtensaTools.ts | 5 +- src/espIdf/unitTest/configure.ts | 8 +- src/espMatter/espMatterDownload.ts | 38 +- src/espMdf/espMdfDownload.ts | 2 +- src/extension.ts | 536 +-- src/flash/dfu.ts | 5 +- src/flash/flashTask.ts | 15 +- src/idfComponentsDataProvider.ts | 9 +- src/idfConfiguration.ts | 6 + src/idfToolsManager.ts | 47 +- src/installManager.ts | 489 --- src/logger/logger.ts | 2 +- src/newProject/newProjectInit.ts | 67 +- src/newProject/newProjectPanel.ts | 6 +- src/newProject/utils.ts | 38 +- src/pythonManager.ts | 285 +- src/qemu/qemuManager.ts | 19 +- .../download/espRainmakerDownload.ts | 2 +- src/setup/SetupPanel.ts | 794 ----- src/setup/addOpenOcdRules.ts | 47 - src/setup/embedGitPy.ts | 206 -- src/setup/espIdfDownload.ts | 163 - src/setup/espIdfDownloadStep.ts | 125 - src/setup/espIdfJson.ts | 144 - src/setup/espIdfVersionList.ts | 323 -- src/setup/existingIdfSetups.ts | 121 - src/setup/installPyReqs.ts | 100 - src/setup/pyReqsInstallStep.ts | 73 - src/setup/setupInit.ts | 349 -- src/setup/setupValidation/espIdfSetup.ts | 122 - src/setup/setupValidation/pythonEnv.ts | 52 - src/setup/toolInstall.ts | 78 - src/setup/toolsDownloadStep.ts | 75 - src/setup/webviewMsgMethods.ts | 124 - src/statusBar/index.ts | 10 +- src/support/checkExtensionRequirements.ts | 44 - src/support/checkIdfSetups.ts | 38 + src/support/checkSpacesInSettings.ts | 4 - src/support/configurationAccess.ts | 4 - src/support/configurationSettings.ts | 75 +- src/support/index.ts | 75 +- src/support/initReportObj.ts | 8 +- src/support/projectConfiguration.ts | 2 +- src/support/troubleshootPanel.ts | 4 +- src/support/types.ts | 12 +- src/support/writeReport.ts | 68 +- src/test/adapter.test.ts | 61 - src/test/doctor.test.ts | 77 +- src/test/espIdfDebugClient.ts | 160 - src/test/mockUtils.ts | 40 + src/test/project.test.ts | 31 +- src/test/suite/downloadManager.test.ts | 237 -- src/test/suite/idfToolsManager.test.ts | 6 + src/test/suite/index.ts | 19 +- src/test/suite/nvsPartitionTable.test.ts | 27 +- src/ui-test/.mocharc-ci.js | 5 + src/ui-test/configure-test.ts | 276 -- src/ui-test/project-build-test.ts | 27 +- src/uninstall.ts | 4 +- src/utils.ts | 316 +- src/versionSwitcher/index.ts | 86 +- src/views/commons/espCommons.scss | 2 +- src/views/menuconfig/Menuconfig.vue | 2 +- src/views/new-project/App.vue | 2 +- src/views/new-project/Configure.vue | 2 +- src/views/new-project/Templates.vue | 2 +- .../new-project/components/folderOpen.vue | 2 +- .../new-project/components/templateList.vue | 2 +- src/views/newSize/App.vue | 2 +- src/views/nvs-partition-table/App.vue | 15 +- src/views/nvs-partition-table/util.ts | 2 +- src/views/partition-table/App.vue | 2 +- .../project-conf/components/SelectElement.vue | 2 +- src/views/setup/App.vue | 121 - src/views/setup/ExistingSetup.vue | 59 - src/views/setup/Home.vue | 191 - src/views/setup/Install.vue | 213 -- src/views/setup/Status.vue | 168 - src/views/setup/ToolsCustom.vue | 100 - src/views/setup/Welcome.vue | 286 -- src/views/setup/components/DownloadStatus.vue | 51 - src/views/setup/components/folderOpen.vue | 81 - src/views/setup/components/logo.vue | 51 - src/views/setup/components/selectEspIdf.vue | 222 -- .../setup/components/selectPyVersion.vue | 78 - .../setup/components/selectSaveScope.vue | 33 - .../setup/components/statusSection/espIdf.vue | 79 - .../components/statusSection/espIdfTools.vue | 54 - .../statusSection/prerequisites.vue | 90 - .../setup/components/statusSection/pyPkgs.vue | 46 - src/views/setup/components/toolDownload.vue | 93 - src/views/setup/components/toolManual.vue | 64 - src/views/setup/main.ts | 292 -- src/views/setup/store.ts | 514 --- src/views/size/App.vue | 2 +- src/views/size/components/ArchiveItem.vue | 2 +- src/views/size/components/FileTable.vue | 4 +- src/views/size/components/Overview.vue | 2 +- src/views/system-view/App.vue | 2 +- src/views/tracing/App.vue | 2 +- src/views/tracing/components/CallStack.vue | 2 +- src/views/tracing/components/LeakList.vue | 2 +- src/views/welcome/App.vue | 106 +- src/welcome/panel.ts | 2 +- src/welcome/welcomeInit.ts | 10 +- templates/.devcontainer/devcontainer.json | 2 - templates/.vscode/c_cpp_properties.json | 4 - .../.vscode/c_cpp_properties.json | 4 - testFiles/testWorkspace/.vscode/settings.json | 9 +- .../testWorkspace/main/hello_world_main.c | 1 - walkthroughs/basic/configuration.md | 27 +- webpack.config.js | 2 +- yarn.lock | 3087 ++++++++--------- 200 files changed, 4446 insertions(+), 11982 deletions(-) delete mode 100644 .github/actions/idf/Dockerfile delete mode 100644 .github/actions/idf/action.yml delete mode 100644 .github/actions/idf/entrypoint.sh delete mode 100644 .github/actions/idf/ui-entrypoint.sh delete mode 100644 export.bat delete mode 100644 media/walkthrough/express-setup.png delete mode 100644 media/walkthrough/idf-version.png delete mode 100644 media/walkthrough/install-btn.png delete mode 100644 media/walkthrough/python-selection.png delete mode 100644 src/checkExtensionSettings.ts create mode 100644 src/common/prepareEnv.ts delete mode 100644 src/downloadManager.ts create mode 100644 src/eim/downloadInstall.ts create mode 100644 src/eim/getExistingSetups.ts create mode 100644 src/eim/loadIdfSetup.ts create mode 100644 src/eim/loadSettings.ts create mode 100644 src/eim/migrationTool.ts rename src/{views/setup => eim}/types.ts (51%) create mode 100644 src/eim/verifySetup.ts delete mode 100644 src/espIdf/debugAdapter/checkPyReqs.ts delete mode 100644 src/espIdf/debugAdapter/debugAdapterManager.ts delete mode 100644 src/installManager.ts delete mode 100644 src/setup/SetupPanel.ts delete mode 100644 src/setup/addOpenOcdRules.ts delete mode 100644 src/setup/embedGitPy.ts delete mode 100644 src/setup/espIdfDownload.ts delete mode 100644 src/setup/espIdfDownloadStep.ts delete mode 100644 src/setup/espIdfJson.ts delete mode 100644 src/setup/espIdfVersionList.ts delete mode 100644 src/setup/existingIdfSetups.ts delete mode 100644 src/setup/installPyReqs.ts delete mode 100644 src/setup/pyReqsInstallStep.ts delete mode 100644 src/setup/setupInit.ts delete mode 100644 src/setup/setupValidation/espIdfSetup.ts delete mode 100644 src/setup/setupValidation/pythonEnv.ts delete mode 100644 src/setup/toolInstall.ts delete mode 100644 src/setup/toolsDownloadStep.ts delete mode 100644 src/setup/webviewMsgMethods.ts delete mode 100644 src/support/checkExtensionRequirements.ts create mode 100644 src/support/checkIdfSetups.ts delete mode 100644 src/test/adapter.test.ts delete mode 100644 src/test/espIdfDebugClient.ts create mode 100644 src/test/mockUtils.ts delete mode 100644 src/test/suite/downloadManager.test.ts create mode 100644 src/ui-test/.mocharc-ci.js delete mode 100644 src/ui-test/configure-test.ts delete mode 100644 src/views/setup/App.vue delete mode 100644 src/views/setup/ExistingSetup.vue delete mode 100644 src/views/setup/Home.vue delete mode 100644 src/views/setup/Install.vue delete mode 100644 src/views/setup/Status.vue delete mode 100644 src/views/setup/ToolsCustom.vue delete mode 100644 src/views/setup/Welcome.vue delete mode 100644 src/views/setup/components/DownloadStatus.vue delete mode 100644 src/views/setup/components/folderOpen.vue delete mode 100644 src/views/setup/components/logo.vue delete mode 100644 src/views/setup/components/selectEspIdf.vue delete mode 100644 src/views/setup/components/selectPyVersion.vue delete mode 100644 src/views/setup/components/selectSaveScope.vue delete mode 100644 src/views/setup/components/statusSection/espIdf.vue delete mode 100644 src/views/setup/components/statusSection/espIdfTools.vue delete mode 100644 src/views/setup/components/statusSection/prerequisites.vue delete mode 100644 src/views/setup/components/statusSection/pyPkgs.vue delete mode 100644 src/views/setup/components/toolDownload.vue delete mode 100644 src/views/setup/components/toolManual.vue delete mode 100644 src/views/setup/main.ts delete mode 100644 src/views/setup/store.ts diff --git a/.github/actions/idf/Dockerfile b/.github/actions/idf/Dockerfile deleted file mode 100644 index 6a6de4aa0..000000000 --- a/.github/actions/idf/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM espressif/idf:release-v4.4 - -ARG DEBIAN_FRONTEND=nointeractive - -RUN apt-get update && apt-get install -y \ - curl \ - libasound2 \ - libgbm-dev \ - libgtk-3-0 \ - libnotify4 \ - libnss3 \ - libsecret-1-0 \ - libxss1 \ - libxtst6 \ - gnome-keyring \ - xvfb - -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 -ENV DISPLAY=":99" -ENV CODE_TESTS_PATH="out/test" -ENV CODE_VERSION="min" - -ENV NVM_DIR /usr/local/nvm -RUN mkdir -p $NVM_DIR -RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash -ENV NODE_VERSION v18.19.1 -RUN . "${NVM_DIR}/nvm.sh" && nvm install ${NODE_VERSION} && nvm use --delete-prefix ${NODE_VERSION} - -ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/lib/node_modules -ENV PATH $NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH -RUN node --version -RUN npm --version -RUN npm install --global typescript yarn - -ADD entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - -ADD ui-entrypoint.sh /ui-entrypoint.sh -RUN chmod +x /ui-entrypoint.sh - - -ENTRYPOINT ["/bin/bash"] -CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/.github/actions/idf/action.yml b/.github/actions/idf/action.yml deleted file mode 100644 index 778802bba..000000000 --- a/.github/actions/idf/action.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: "ESP-IDF" -description: "run tests on ESP-IDF docker image" - -inputs: - run: - description: "command for docker image" - required: true - default: /entrypoint.sh -outputs: - result: - description: "Docker test output" - uiresult: - description: "UI test output" - -runs: - using: "docker" - image: "Dockerfile" - args: - - ${{ inputs.run }} diff --git a/.github/actions/idf/entrypoint.sh b/.github/actions/idf/entrypoint.sh deleted file mode 100644 index 84a59870c..000000000 --- a/.github/actions/idf/entrypoint.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -e - -export OLD_PATH=$PATH - -. $IDF_PATH/export.sh - -cd /github/workspace - -pip install --upgrade pip -pip install --constraint espidf.constraints.txt -r esp_debug_adapter/requirements.txt - -export GIT_VERSION=$( echo "$a" | echo $(git --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export IDF_VERSION=$( echo "$a" | echo $(idf.py --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PY_VERSION=$( echo "$a" | echo $(python --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PIP_VERSION=$( echo "$a" | echo $(python -m pip --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PY_PKGS=$(python -m pip list --format json) - -yarn -yarn lint -Xvfb -ac :99 -screen 0 1920x1080x16 & sleep 2 & yarn test -EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) -echo "result<<$EOF" >> $GITHUB_OUTPUT -echo "$(cat ./out/results/test-results.xml)" >> $GITHUB_OUTPUT -echo "$EOF" >> $GITHUB_OUTPUT - -rm -r .vscode-test \ No newline at end of file diff --git a/.github/actions/idf/ui-entrypoint.sh b/.github/actions/idf/ui-entrypoint.sh deleted file mode 100644 index 01b981e33..000000000 --- a/.github/actions/idf/ui-entrypoint.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -e - -export OLD_PATH=$PATH - -. $IDF_PATH/export.sh - -cd /github/workspace - -pip install --upgrade pip -pip install --constraint espidf.constraints.txt -r esp_debug_adapter/requirements.txt - -export GIT_VERSION=$( echo "$a" | echo $(git --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export IDF_VERSION=$( echo "$a" | echo $(idf.py --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PY_VERSION=$( echo "$a" | echo $(python --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PIP_VERSION=$( echo "$a" | echo $(python -m pip --version) | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') -export PY_PKGS=$(python -m pip list --format json) - -rm -rf node_modules out test-resources -yarn -yarn lint -Xvfb -ac :99 -screen 0 1920x1080x16 & sleep 2 & yarn ci-test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 544df910f..2463e59da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,29 +16,52 @@ jobs: - uses: actions/checkout@v4 with: submodules: "recursive" - - uses: actions/setup-node@v4 + + - name: Install ESP-IDF + run: | + ESP_IDF_VERSION="v5.3.2" + sudo apt update + sudo apt install -y python3-pip git wget flex bison gperf python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util + wget https://dl.espressif.com/github_assets/espressif/esp-idf/releases/download/${ESP_IDF_VERSION}/esp-idf-${ESP_IDF_VERSION}.zip -O esp-idf.zip + unzip esp-idf.zip -d ~/ + rm esp-idf.zip + mv ~/esp-idf-${ESP_IDF_VERSION} ~/esp-idf + ~/esp-idf/install.sh + + - name: Setup Node.js + uses: actions/setup-node@v4 with: node-version: 20 - - name: Install Node Dependencies - run: yarn + - name: Install Yarn dependencies + run: | + yarn + yarn install - - name: Lint Check - run: yarn lint + - name: Run Yarn script with xvfb + run: | + source ~/esp-idf/export.sh + export GIT_VERSION=$(git --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') + export IDF_VERSION=$(idf.py --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') + export PY_VERSION=$(python --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') + export PIP_VERSION=$(python -m pip --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p') + export PY_PKGS=$(python -m pip list --format json) + xvfb-run --auto-servernum yarn test - - name: Run test - uses: ./.github/actions/idf - id: idftest - - - name: Get the test output - run: echo "${{ steps.idftest.outputs.result }}" | tee test-result.xml + - name: Publish Test Report + uses: dorny/test-reporter@v2 + if: always() + with: + name: Mocha Tests + path: ./out/results/test-results.json + reporter: mocha-json - name: Upload if failed test results uses: actions/upload-artifact@v4 if: failure() with: - name: test-result.xml - path: test-result.xml + name: test-result.json + path: ./out/results/test-results.json - name: Package .vsix run: yarn package diff --git a/.github/workflows/ui-test.yml b/.github/workflows/ui-test.yml index 5d27eb9ea..a6fd47b09 100644 --- a/.github/workflows/ui-test.yml +++ b/.github/workflows/ui-test.yml @@ -42,7 +42,92 @@ jobs: run: | export IDF_TOOLS_PATH=~/.espressif export IDF_PATH=~/esp-idf - source ~/esp-idf/export.sh && xvfb-run --auto-servernum yarn ci-test + source ~/esp-idf/export.sh && xvfb-run --auto-servernum yarn ci-test 2>&1 | tee test-output.log + + # Extract JSON from the output and save to file + echo "Extracting JSON from test output..." + if grep -q "Extension 'espressif.esp-idf-extension' was successfully uninstalled!" test-output.log; then + sed -n '/Extension '\''espressif.esp-idf-extension'\'' was successfully uninstalled!/,$p' test-output.log > after-uninstall.log + grep -n '{' after-uninstall.log | head -1 | cut -d: -f1 > start-line.txt + grep -n '}' after-uninstall.log | tail -1 | cut -d: -f1 > end-line.txt + if [ -s start-line.txt ] && [ -s end-line.txt ]; then + start_line=$(cat start-line.txt) + end_line=$(cat end-line.txt) + sed -n "${start_line},${end_line}p" after-uninstall.log > ./out/ui-test-results.json + sed 's/}[^,]*$/}/' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/INFO:/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/Screenshots of failures can be found in:/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\x1b\[/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\[33m/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\[0m/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/test-resources\/screenshots\//d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + echo "JSON results extracted and saved to ./out/ui-test-results.json" + echo "Debug: Last 10 lines of the JSON file:" + tail -10 ./out/ui-test-results.json + else + echo "Could not find JSON boundaries" + fi + rm -f after-uninstall.log start-line.txt end-line.txt ./out/temp.json + else + echo "Uninstall message not found, trying to extract JSON from entire output..." + if grep -q '{' test-output.log; then + grep -n '{' test-output.log | head -1 | cut -d: -f1 > start-line.txt + grep -n '}' test-output.log | tail -1 | cut -d: -f1 > end-line.txt + if [ -s start-line.txt ] && [ -s end-line.txt ]; then + start_line=$(cat start-line.txt) + end_line=$(cat end-line.txt) + sed -n "${start_line},${end_line}p" test-output.log > ./out/ui-test-results.json + sed 's/}[^,]*$/}/' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/INFO:/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/Screenshots of failures can be found in:/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\x1b\[/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\[33m/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/\[0m/d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + sed '/test-resources\/screenshots\//d' ./out/ui-test-results.json > ./out/temp.json && mv ./out/temp.json ./out/ui-test-results.json + echo "JSON results extracted and saved to ./out/ui-test-results.json" + echo "Debug: Last 10 lines of the JSON file:" + tail -10 ./out/ui-test-results.json + else + echo "Could not find JSON boundaries" + fi + rm -f start-line.txt end-line.txt ./out/temp.json + else + echo "No JSON found in test output" + fi + fi + + - name: Print UI Test Results + run: | + echo "=== UI Test Results ===" + if [ -f "./out/ui-test-results.json" ]; then + echo "UI Test Results:" + cat ./out/ui-test-results.json | jq '.' || echo "Could not parse UI test results" + elif [ -f "./test-results.json" ]; then + echo "UI Test Results:" + cat ./test-results.json | jq '.' || echo "Could not parse UI test results" + else + echo "No UI test results file found" + echo "Checking for other test output files:" + find . -name "*test*results*" -o -name "*ui-test-results.json*" | head -10 + fi + + - name: Publish UI Test Report + uses: dorny/test-reporter@v2 + if: always() + with: + name: UI Tests + path: ./out/ui-test-results.json + reporter: mocha-json + + - name: Upload UI test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: ui-test-results + path: | + ./out/ui-test-results.json + ./test-resources/screenshots/ + test-output.log - name: Upload screenshots uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 258971fdd..ab8685cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ testFiles/blink !testFiles/dist testFiles/tools testFiles/testWorkspace/build -testFiles/testWorkspace/sdkconfig.* +testFiles/testWorkspace/sdkconfig* *.log .DS_Store bandit_output.txt diff --git a/README.md b/README.md index d5fea4b95..32e1e60b4 100644 --- a/README.md +++ b/README.md @@ -39,31 +39,19 @@ Make sure to review our [Espressif documentation](https://docs.espressif.com/pro Commands list

-6. From the command list, select **Configure ESP-IDF Extension** or press F1 and type `Configure ESP-IDF Extension`. After, choose the **ESP-IDF: Configure ESP-IDF Extension** option. +6. From the command list, select **ESP-IDF: Open ESP-IDF Install Manager** or press F1 and type `Open ESP-IDF Install Manager`. After, choose the **ESP-IDF: ESP-IDF: Open ESP-IDF Install Manager** option. > **NOTE:** For versions of ESP-IDF < 5.0, spaces are not supported inside configured paths. -

- Select ESP-IDF -

- -7. Choose **Express** and select the download server: +7. Alternatively, you can download the ESP-IDF Install Manager from the following link [ESP-IDF Install Manager](https://dl.espressif.com/dl/eim/index.html) among the following options:: - Espressif: Faster speed in China using Espressif download servers links. - Github: Using github releases links. -8. Pick an ESP-IDF version to download or the `Find ESP-IDF in your system` option to search for existing ESP-IDF directory. - -9. Choose the location for ESP-IDF Tools (also known as `IDF_TOOLS_PATH`) which is `$HOME\.espressif` on MacOS/Linux and `%USERPROFILE%\.espressif` on Windows by default. - -10. If your operating system is MacOS/Linux, choose the system Python executable to create ESP-IDF virtual environment inside ESP-IDF Tools and install ESP-IDF Python package there. - - > **NOTE:** Windows users don't need to select a Python executable since it is going to be installed by this setup. +8. Use the ESP-IDF Install Manager to install the ESP-IDF and tools. If necessary, here is the [ESP-IDF Install Manager Documentation](https://docs.espressif.com/projects/idf-im-ui/en/latest/general_info.html). -11. Make sure that `IDF_TOOLS_PATH` doesn't have any spaces to avoid any build issues. Also make sure that `IDF_TOOLS_PATH` is not the same directory as `IDF_PATH`. +9. In Visual Studio Code, navigate to `View` > `Command Palette` and type `select current esp-idf version` and select **ESP-IDF: Select Current ESP-IDF Version** from the list. The list of available ESP-IDF setups will be shown, select which one you want to use for the current ESP-IDF project. The selected setup will save a **idf.currentSetup** with selected ESP-IDF path and the extension will configure environment variables for the current project saved as workspace folder state. You can review the setup by running the **ESP-IDF: Doctor Command** by navigate to `View` > `Command Palette` and type `doctor command` and select **ESP-IDF: ESP-IDF: Doctor Command** from the list. -12. You will see a page showing the setup progress status, including ESP-IDF download progress, ESP-IDF Tools download and install progress as well as the creation of a Python virtual environment. - -13. If everything is installed correctly, you will see a message that all settings have been configured. You can start using the extension. +10. If everything is installed correctly, you will see a message that all settings have been configured. You can start using the extension. Check the [Troubleshooting](#Troubleshooting) section if you have any issues. @@ -323,7 +311,7 @@ Press F1 or click menu `View` -> `Command Palette...` to show Visual Additional frameworks Install ESP-ADF - Clone ESP-ADF inside the selected directory and set idf.espAdfPath (idf.espAdfPathWin in Windows) configuration setting. + Clone ESP-ADF inside the selected directory and set ADF_PATH in idf.customExtraVars configuration setting. @@ -342,13 +330,13 @@ Press F1 or click menu `View` -> `Command Palette...` to show Visual Install ESP-MDF - Clone ESP-MDF inside the selected directory and set idf.espMdfPath (idf.espMdfPathWin in Windows) configuration setting. + Clone ESP-MDF inside the selected directory and set MDF_PATH in idf.customExtraVars configuration setting. Install ESP-Matter - Clone ESP-Matter and set idf.espMatterPath. ESP-Matter is not supported in Windows. Make sure to install Matter system prerequisites first. + Clone ESP-Matter and set ESP_MATTER_PATH in idf.customExtraVars. ESP-Matter is not supported in Windows. Make sure to install Matter system prerequisites first. @@ -360,13 +348,13 @@ Press F1 or click menu `View` -> `Command Palette...` to show Visual Install ESP-Rainmaker - Clone ESP-Rainmaker and set idf.espRainmakerPath (idf.espRainmakerPathWin in Windows) configuration setting. + Clone ESP-Rainmaker and set RMAKER_PATH in idf.customExtraVars. Install ESP-HomeKit-SDK - Clone ESP-HomeKit-SDK inside the selected directory and set idf.espHomeKitSdkPath (idf.espHomeKitSdkPathWin in Windows) configuration setting. + Clone ESP-HomeKit-SDK inside the selected directory and set HOMEKIT_PATH in idf.customExtraVars configuration setting. @@ -528,12 +516,6 @@ Press F1 or click menu `View` -> `Command Palette...` to show Visual - - Clear Saved ESP-IDF Setups - Clear existing ESP-IDF setups saved by the extension. - - - @@ -604,7 +586,7 @@ This extension uses the ``idf.saveScope`` configuration setting (which can only 7. In some cases, the default shell (Powershell, zsh, sh, .etc) configured in VS Code could affect the behavior of the extension. Make sure that MSYS/MinGW is not set in the environment and the variables don't conflict with terminal behavior. The `ESP-IDF: Doctor Command` shows which shell is detected by the extension when running tasks like building, flashing and monitoring. More information in [here](https://code.visualstudio.com/docs/terminal/profiles). -If there is any Python package error, please try to reinstall the required Python packages with the **ESP-IDF: Install ESP-IDF Python Packages** command or running the setup again with the **ESP-IDF: Configure ESP-IDF Extension** command. +If there is any Python package error, please try to reinstall the required Python packages with the [ESP-IDF Installation Manager](https://docs.espressif.com/projects/idf-im-ui/en/latest/general_info.html). > **NOTE:** When downloading ESP-IDF using git cloning in Windows, if you receive errors like "unable to create symlink", enabling `Developer Mode` while cloning ESP-IDF could help resolve the issue. diff --git a/README_CN.md b/README_CN.md index 6c18da8f8..99d76f812 100644 --- a/README_CN.md +++ b/README_CN.md @@ -323,7 +323,7 @@ ESP-IDF 扩展在 VS Code 底部蓝色窗口的状态栏中提供了一系列命 可集成软件框架 安装 ESP-ADF - 在所选目录中克隆 ESP-ADF,并配置 idf.espAdfPath(Windows 系统中为 idf.espAdfPathWin)。 + 在所选目录中克隆 ESP-ADF,并配置 ADF_PATH 在 idf.customExtraVars。 @@ -342,13 +342,13 @@ ESP-IDF 扩展在 VS Code 底部蓝色窗口的状态栏中提供了一系列命 安装 ESP-MDF - 在所选目录中克隆 ESP-MDF,并配置 idf.espMdfPath(Windows 系统中为 idf.espMdfPathWin)。 + 在所选目录中克隆 ESP-MDF,并配置 MDF_PATH 在 idf.customExtraVars。 安装 ESP-Matter - 克隆 ESP-Matter 并配置 idf.espMatterPath。Windows 系统不支持 ESP-Matter。运行该命令前请确保已安装 Matter 系统依赖项。 + 克隆 ESP-Matter 并配置 ESP_MATTER_PATH 在 idf.customExtraVars。Windows 系统不支持 ESP-Matter。运行该命令前请确保已安装 Matter 系统依赖项。 @@ -360,13 +360,13 @@ ESP-IDF 扩展在 VS Code 底部蓝色窗口的状态栏中提供了一系列命 安装 ESP-Rainmaker - 克隆 ESP-Rainmaker,并配置 idf.espRainmakerPath(Windows 系统中为 idf.espRainmakerPathWin)。 + 克隆 ESP-Rainmaker,并配置 RMAKER_PATH 在 idf.customExtraVars。 安装 ESP-HomeKit-SDK - 在所选目录中克隆 ESP-HomeKit-SDK,并配置 idf.espHomeKitSdkPath(Windows 系统中为 idf.espHomeKitSdkPathWin)。 + 在所选目录中克隆 ESP-HomeKit-SDK,并配置 HOMEKIT_PATH 在 idf.customExtraVars。 diff --git a/docs_espressif/en/additionalfeatures/docker-container.rst b/docs_espressif/en/additionalfeatures/docker-container.rst index 21778b240..258962192 100644 --- a/docs_espressif/en/additionalfeatures/docker-container.rst +++ b/docs_espressif/en/additionalfeatures/docker-container.rst @@ -207,8 +207,6 @@ For more information about ``devcontainer.json``, please refer to the comments. */ "settings": { "terminal.integrated.defaultProfile.linux": "bash", - "idf.espIdfPath": "/opt/esp/idf", - "idf.toolsPath": "/opt/esp", "idf.gitPath": "/usr/bin/git" }, /* An array of extensions that should be installed into the container. */ diff --git a/docs_espressif/en/additionalfeatures/multiple-projects.rst b/docs_espressif/en/additionalfeatures/multiple-projects.rst index 8dead8011..1d6e17e72 100644 --- a/docs_espressif/en/additionalfeatures/multiple-projects.rst +++ b/docs_espressif/en/additionalfeatures/multiple-projects.rst @@ -25,20 +25,19 @@ Project folders (known in VS Code as workspace folders) and workspace-level sett .. code-block:: JSON - { - "folders": [ - { - "path": "./project1" - }, - { - "path": "./project2" - } - ], - "settings": { - "idf.port": "/dev/ttyUSB1", - "idf.espIdfPath": "${env:HOME}/esp/esp-idf" - } + { + "folders": [ + { + "path": "./project1" + }, + { + "path": "./project2" + } + ], + "settings": { + "idf.port": "/dev/ttyUSB1", } + } Settings in the root folder's ``.code-workspace`` are used when your ``ESP-IDF: Current Project`` directory lacks a ``.vscode/settings.json`` file. @@ -69,20 +68,19 @@ And ``my-ws.code-workspace``: .. code-block:: JSON - { - "folders": [ - { - "path": "/my-projects-root/project1" - }, - { - "path": "/my-projects-root/project2" - } - ], - "settings": { - "idf.port": "/dev/ttyUSB1", - "idf.espIdfPath": "${env:HOME}/esp/esp-idf" - } + { + "folders": [ + { + "path": "/my-projects-root/project1" + }, + { + "path": "/my-projects-root/project2" + } + ], + "settings": { + "idf.port": "/dev/ttyUSB1", } + } 1. Open Visual Studio Code, go to ``File`` > ``Open Workspace`` and open ``my-ws.code-workspace``, you will see only the folders defined in this workspace (``/my-projects-root/project1`` and ``/my-projects-root/project2``). diff --git a/docs_espressif/en/additionalfeatures/web-extension.rst b/docs_espressif/en/additionalfeatures/web-extension.rst index 2c25e065a..65c0c1fe4 100644 --- a/docs_espressif/en/additionalfeatures/web-extension.rst +++ b/docs_espressif/en/additionalfeatures/web-extension.rst @@ -33,9 +33,6 @@ You can also configure a github ESP-IDF project for Codespaces with the ESP-IDF "vscode": { "settings": { "terminal.integrated.defaultProfile.linux": "bash", - "idf.espIdfPath": "/opt/esp/idf", - "idf.customExtraPaths": "", - "idf.toolsPath": "/opt/esp", "idf.gitPath": "/usr/bin/git", "idf.showOnboardingOnInit": false, "extensions.ignoreRecommendations": true diff --git a/docs_espressif/en/commands.rst b/docs_espressif/en/commands.rst index 92afae9b4..28d7a8e01 100644 --- a/docs_espressif/en/commands.rst +++ b/docs_espressif/en/commands.rst @@ -26,10 +26,6 @@ All commands start with ``ESP-IDF:``. - Clear the eFuse Summary tree from ESP Explorer EFUSEEXPLORER. * - Clear ESP-IDF Search Results - Clear results from ESP Explorer Documentation Search Results. - * - Clear Saved ESP-IDF Setups - - Clear existing ESP-IDF setups saved by the extension. - * - Configure ESP-IDF Extension - - Open a window with a setup wizard to install ESP-IDF, IDF Tools and Python virtual environment. * - Configure Project SDKConfig for Coverage - Set required values in your project SDKConfig to enable code coverage analysis. * - Configure project for ESP-Clang @@ -67,17 +63,17 @@ All commands start with ``ESP-IDF:``. * - Import ESP-IDF Project - Import an existing ESP-IDF project, add .vscode and .devcontainer files to a new location, and optionally rename the project. * - Install ESP-ADF - - Clone ESP-ADF inside the selected directory and set **idf.espAdfPath** (**idf.espAdfPathWin** in Windows) configuration setting. + - Clone ESP-ADF inside the selected directory and set **ADF_PATH** in **idf.customExtraVars** configuration setting. * - Install ESP-IDF Python Packages (DEPRECATION NOTICE) - Install extension Python packages. This command is deprecated and will be removed soon. * - Install ESP-MDF - - Clone ESP-MDF inside the selected directory and set **idf.espMdfPath** (**idf.espMdfPathWin** in Windows) configuration setting. + - Clone ESP-MDF inside the selected directory and set **MDF_PATH** in **idf.customExtraVars** configuration setting. * - Install ESP-Matter - - Clone ESP-Matter and set **idf.espMatterPath**. ESP-Matter is not supported on Windows. + - Clone ESP-Matter and set **ESP_MATTER_PATH** in **idf.customExtraVars**. ESP-Matter is not supported on Windows. * - Install ESP-Rainmaker - - Clone ESP-Rainmaker and set **idf.espRainmakerPath** (**idf.espRainmakerPathWin** in Windows) configuration setting. + - Clone ESP-Rainmaker and set **RMAKER_PATH** in **idf.customExtraVars** configuration setting. * - Install ESP-HomeKit-SDK - - Clone ESP-HomeKit-SDK inside the selected directory and set **idf.espHomeKitSdkPath** (**idf.espHomeKitSdkPathWin** in Windows) configuration setting. + - Clone ESP-HomeKit-SDK inside the selected directory and set **HOMEKIT_PATH** in **idf.customExtraVars** configuration setting. * - Launch IDF Monitor for Core Dump Mode/GDB Stub Mode - Launch ESP-IDF Monitor with WebSocket capabilities. If you have configured the panic handler to gdbstub or core dump, the monitor will launch a post-mortem debug session of the chip. * - Launch QEMU Server @@ -94,6 +90,8 @@ All commands start with ``ESP-IDF:``. - Launch UI to create a CSV file for `ESP-IDF Non-Volatile Storage Library `_. * - Open ESP-IDF Terminal - Open a terminal with IDF_PATH and Python virtual environment activated. + * - Open ESP-IDF Install Manager + - Open the ESP-IDF Install Manager to install and manage ESP-IDF versions and tools. * - Partition Table Editor - Launch UI to manage custom partition table as described in `ESP-IDF Partition Tables `_. * - Pick a Workspace Folder diff --git a/docs_espressif/en/configureproject.rst b/docs_espressif/en/configureproject.rst index 912f9d2bc..134151f6a 100644 --- a/docs_espressif/en/configureproject.rst +++ b/docs_espressif/en/configureproject.rst @@ -55,26 +55,24 @@ The file structure is as follows: .. code-block:: JSON - { - "configurations": [ - { - "name": "ESP-IDF", - "compilerPath": "/path/to/toolchain-gcc", - "compileCommands": "${workspaceFolder}/build/compile_commands.json", - "includePath": [ - "${config:idf.espIdfPath}/components", - "${config:idf.espIdfPathWin}/components/", - "${workspaceFolder}/" - ], - "browse": { - "path": [ - "${config:idf.espIdfPath}/components", - "${config:idf.espIdfPathWin}/components", - "${workspaceFolder}" - ] - } - } - ] - } + { + "configurations": [ + { + "name": "ESP-IDF", + "compilerPath": "/path/to/toolchain-gcc", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "includePath": [ + "/path/to/esp-idf/components/**", + "${workspaceFolder}/**" + ], + "browse": { + "path": [ + "/path/to/esp-idf/components", + "${workspaceFolder}" + ] + } + } + ] + } If ``compile_commands.json`` is not defined, Microsoft C/C++ extension will browse the provided ESP-IDF path to resolve code navigation. diff --git a/docs_espressif/en/installation.rst b/docs_espressif/en/installation.rst index d89798e9e..1242668a3 100644 --- a/docs_espressif/en/installation.rst +++ b/docs_espressif/en/installation.rst @@ -15,52 +15,27 @@ After installing Visual Studio Code (VS Code), install the ESP-IDF extension for - Navigate to ``View`` > ``Command Palette``. - - Type ``ESP-IDF: Configure ESP-IDF Extension`` and select the command to launch the setup wizard. A loading notification will appear, followed by the setup wizard. + - Type ``ESP-IDF: Open ESP-IDF Install Manager`` to download and run the ESP-IDF install manager to install the ESP-IDF framework. A loading notification will appear, followed by the execution of the installer. .. note:: For versions of ESP-IDF < 5.0, spaces are not supported in configured paths. - .. image:: ../../media/tutorials/setup/select-mode.png - -2. Choose ``Express`` and select the download server: +2. Alternatively, you can download the ESP-IDF Install Manager from the following link `ESP-IDF Install Manager `_ among the following options: - ``Espressif``: Faster speed in China using Espressif download servers. - ``Github``: Using GitHub release links. -3. Pick an ESP-IDF version to download or use the ``Find ESP-IDF in your system`` option to search for an existing ESP-IDF directory. - - .. image:: ../../media/tutorials/setup/select-esp-idf.png - - Choose the location for ESP-IDF Tools (``IDF_TOOLS_PATH``), which defaults to ``%USERPROFILE%\.espressif`` on Windows and ``$HOME\.espressif`` on macOS/Linux. - - .. note:: - - Make sure that ``IDF_TOOLS_PATH`` does not contain spaces to avoid build issues. Also, ensure that ``IDF_TOOLS_PATH`` is not the same directory as ``IDF_PATH``. - - .. note:: - - For macOS/Linux users, select the Python executable to create the ESP-IDF Python virtual environment. - -4. Click ``Install`` to begin downloading and installing ESP-IDF and ESP-IDF Tools. - -5. A page will appear showing the setup progress status: - - - ESP-IDF download progress - - ESP-IDF Tools download and installation progress - - Creation of a Python virtual environment and installation of ESP-IDF Python requirements +3. Use the ESP-IDF Install Manager to install the ESP-IDF and tools. If necessary, here is the `ESP-IDF Install Manager Documentation `_. - .. image:: ../../media/tutorials/setup/install-status.png - -6. If everything installs correctly, you will see a message indicating that all settings have been configured. - - .. image:: ../../media/tutorials/setup/install-complete.png - - .. note:: +4. In Visual Studio Code, navigate to ``View`` > ``Command Palette`` and type ``select current esp-idf version`` and select **ESP-IDF: Select Current ESP-IDF Version** from the list. + The list of available ESP-IDF setups will be shown, select which one you want to use for the current ESP-IDF project. + + - The selected setup will save a **idf.currentSetup** with selected ESP-IDF path and the extension will configure required environment variables for the current ESP-IDF project saved as workspace folder state. - For Linux users, a message will prompt you to add OpenOCD rules in ``/etc/udev/rules.d``. You need to run this with sudo privileges. + - You can review the setup by running the **ESP-IDF: Doctor Command** by navigate to ``View`` > ``Command Palette`` and type ``doctor command`` and select **ESP-IDF: ESP-IDF: Doctor Command** from the list. -7. The next step is to :ref:`Create an ESP-IDF Project `. +5. The next step is to :ref:`Create an ESP-IDF Project `. .. warning:: @@ -72,7 +47,7 @@ Uninstall ESP-IDF VS Code Extension To uninstall the ESP-IDF VS Code extension, follow these steps: -1. Open Command Palette (press shortcut F1) and type ``ESP-IDF: Clear Saved ESP-IDF Setups``. Select the command to remove all ESP-IDF settings. +1. Open Command Palette (press shortcut F1) and type ``ESP-IDF: Remove ESP-IDF Settings``. Select the command to remove all ESP-IDF settings. 2. Navigate to ``View`` > ``Extensions`` or use the keyboard shortcut :kbd:`Ctrl+Shift+X` in Windows/Linux or :kbd:`Shift+⌘+X` in macOS. diff --git a/docs_espressif/en/settings.rst b/docs_espressif/en/settings.rst index 4592f1ef5..f33622a84 100644 --- a/docs_espressif/en/settings.rst +++ b/docs_espressif/en/settings.rst @@ -25,7 +25,7 @@ This extension contributes the following settings that can be later updated in ` .. note:: - Please note that ``~``, ``%VARNAME%`` and ``$VARNAME`` are not recognized when set on ANY of this extension configuration settings. Instead, you can set any environment variable in the path using ``${env:VARNAME}``, such as ``${env:HOME}``, or refer to other configuration parameter path with ``${config:SETTINGID}``, such as ``${config:idf.espIdfPath}``. + Please note that ``~``, ``%VARNAME%`` and ``$VARNAME`` are not recognized when set on ANY of this extension configuration settings. Instead, you can set any environment variable in the path using ``${env:VARNAME}``, such as ``${env:HOME}``, or refer to other configuration parameter path with ``${config:SETTINGID}``, such as ``${config:idf.buildPath}``. .. note:: @@ -62,26 +62,13 @@ These are the configuration settings that ESP-IDF extension contributes to your - Enable CCache in build task (make sure CCache is in PATH) * - idf.enableIdfComponentManager - Enable IDF Component manager in build command - * - idf.espIdfPath - - Path to locate ESP-IDF framework (IDF_PATH) - * - idf.espIdfPathWin - - Path to locate ESP-IDF framework in Windows (IDF_PATH) * - idf.ninjaArgs - Arguments for Ninja build task - * - idf.pythonInstallPath - - System Python absolute path used to compute ESP-IDF Python virtual environment - * - idf.toolsPath - - Path to locate ESP-IDF Tools (IDF_TOOLS_PATH) - * - idf.toolsPathWin - - Path to locate ESP-IDF Tools in Windows (IDF_TOOLS_PATH) This is how the extension uses them: 1. **idf.customExtraVars** stores any custom environment variable such as OPENOCD_SCRIPTS, which is the openOCD scripts directory used in OpenOCD server startup. These variables are loaded to this extension command's process environment variables, choosing the extension variable if available, else extension commands will try to use what is already in your system PATH. **This doesn't modify your system environment outside Visual Studio Code.** -2. **idf.espIdfPath** (or **idf.espIdfPathWin** in Windows) is used to store ESP-IDF directory path within our extension. We override Visual Studio Code process IDF_PATH if this value is available. **This doesn't modify your system environment outside Visual Studio Code.**. It is also used to compute the list of ESP-IDF tools to add to environment variable PATH and the Python virtual environment path together from **idf.toolsPath** and **idf.pythonInstallPath**. -3. **idf.pythonInstallPath** is the system Python absolute path used to compute ESP-IDF Python virtual environment from **idf.toolsPath** and **idf.espIdfPath** where ESP-IDF Python packages will be installed and used. -4. **idf.gitPath** (or **idf.gitPathWin** in Windows) is used in the extension to clone ESP-IDF master version or the additional supported frameworks such as ESP-ADF, ESP-MDF and Arduino-ESP32. -5. **idf.toolsPath** (or **idf.toolsPathWin** in Windows) is used to compute the list of ESP-IDF tools to add to environment variable PATH and the Python virtual environment path together from **idf.pythonInstallPath** and **idf.espIdfPath**. +2. **idf.gitPath** (or **idf.gitPathWin** in Windows) is used in the extension to clone ESP-IDF master version or the additional supported frameworks such as ESP-ADF, ESP-MDF and Arduino-ESP32. .. note:: @@ -306,20 +293,6 @@ These settings support additional frameworks together with ESP-IDF: * - Setting ID - Description - * - **idf.espAdfPath** - - Path to locate ESP-ADF framework (ADF_PATH) - * - **idf.espAdfPathWin** - - Path to locate ESP-ADF framework in Windows (ADF_PATH) - * - **idf.espMdfPath** - - Path to locate ESP-MDF framework (MDF_PATH) - * - **idf.espMdfPathWin** - - Path to locate ESP-MDF framework in Windows (MDF_PATH) - * - **idf.espMatterPath** - - Path to locate ESP-Matter framework (ESP_MATTER_PATH) - * - **idf.espRainmakerPath** - - Path to locate ESP-Rainmaker framework in Windows (RMAKER_PATH) - * - **idf.espRainmakerPathWin** - - Path to locate ESP-Rainmaker framework in Windows (RMAKER_PATH) * - **idf.sbomFilePath** - Path to create ESP-IDF SBOM report @@ -332,4 +305,4 @@ Environment (env) variables and other ESP-IDF settings (config) can be reference You can also prepend a string to the result of the other ESP-IDF settings (config) by using the syntax ``${config:ESPIDFSETTING:prefix}``. The prefix will be added to the beginning of the variable value. For example ``${config:idf.openOcdConfigs,-f}`` will add ``-f`` to the beginning of the each string value of **idf.openOcdConfigs**. If ``"idf.openOcdConfigs": ["interface/some.cfg", "target/some.cfg"]`` returns ``-f interface/some.cfg -f target/some.cfg``. -For example, to use ``"~/esp/esp-idf"``, set the value of **idf.espIdfPath** to ``"${env:HOME}/esp/esp-idf"``. +For example, to use ``"~/workspace/blink"``, set the value to ``"${env:HOME}/workspace/blink"``. diff --git a/docs_espressif/zh_CN/additionalfeatures/docker-container.rst b/docs_espressif/zh_CN/additionalfeatures/docker-container.rst index b76531d44..657a78380 100644 --- a/docs_espressif/zh_CN/additionalfeatures/docker-container.rst +++ b/docs_espressif/zh_CN/additionalfeatures/docker-container.rst @@ -207,8 +207,6 @@ Docker 中的 ``usbipd-win`` */ "settings": { "terminal.integrated.defaultProfile.linux": "bash", - "idf.espIdfPath": "/opt/esp/idf", - "idf.toolsPath": "/opt/esp", "idf.gitPath": "/usr/bin/git" }, /* 应安装到容器中的一组扩展 */ diff --git a/docs_espressif/zh_CN/additionalfeatures/multiple-projects.rst b/docs_espressif/zh_CN/additionalfeatures/multiple-projects.rst index cdd956576..9de6656a4 100644 --- a/docs_espressif/zh_CN/additionalfeatures/multiple-projects.rst +++ b/docs_espressif/zh_CN/additionalfeatures/multiple-projects.rst @@ -35,8 +35,7 @@ } ], "settings": { - "idf.port": "/dev/ttyUSB1", - "idf.espIdfPath": "${env:HOME}/esp/esp-idf" + "idf.port": "/dev/ttyUSB1" } } @@ -79,8 +78,7 @@ } ], "settings": { - "idf.port": "/dev/ttyUSB1", - "idf.espIdfPath": "${env:HOME}/esp/esp-idf" + "idf.port": "/dev/ttyUSB1" } } diff --git a/docs_espressif/zh_CN/additionalfeatures/web-extension.rst b/docs_espressif/zh_CN/additionalfeatures/web-extension.rst index 2c25e065a..65c0c1fe4 100644 --- a/docs_espressif/zh_CN/additionalfeatures/web-extension.rst +++ b/docs_espressif/zh_CN/additionalfeatures/web-extension.rst @@ -33,9 +33,6 @@ You can also configure a github ESP-IDF project for Codespaces with the ESP-IDF "vscode": { "settings": { "terminal.integrated.defaultProfile.linux": "bash", - "idf.espIdfPath": "/opt/esp/idf", - "idf.customExtraPaths": "", - "idf.toolsPath": "/opt/esp", "idf.gitPath": "/usr/bin/git", "idf.showOnboardingOnInit": false, "extensions.ignoreRecommendations": true diff --git a/docs_espressif/zh_CN/commands.rst b/docs_espressif/zh_CN/commands.rst index 79955af14..75ee6be15 100644 --- a/docs_espressif/zh_CN/commands.rst +++ b/docs_espressif/zh_CN/commands.rst @@ -67,17 +67,17 @@ * - 导入 ESP-IDF 项目 - 导入现有的 ESP-IDF 项目,在新位置添加 .vscode 和 .devcontainer 文件,同时可以重命名项目。 * - 安装 ESP-ADF - - 在所选目录中克隆 ESP-ADF,并配置 **idf.espAdfPath**(Windows 系统中为 **idf.espAdfPathWin**)。 + - 在所选目录中克隆 ESP-ADF,并配置 **ADF_PATH** 在 **idf.customExtraVars**。 * - 安装 ESP-IDF Python 包(已弃用) - 安装扩展 Python 包。本命令已弃用,即将被移除。 * - 安装 ESP-MDF - - 在所选目录中克隆 ESP-MDF,并配置 **idf.espMdfPath**(Windows 系统中为 **idf.espMdfPathWin**)。 + - 在所选目录中克隆 ESP-MDF,并配置 **MDF_PATH** 在 **idf.customExtraVars**。 * - 安装 ESP-Matter - - 克隆 ESP-Matter 并设置 **idf.espMatterPath**。ESP-Matter 不支持 Windows。 + - 克隆 ESP-Matter 并设置 **ESP_MATTER_PATH** 在 **idf.customExtraVars**。ESP-Matter 不支持 Windows。 * - 安装 ESP-Rainmaker - - 克隆 ESP-Rainmaker,并配置 **idf.espRainmakerPath**(Windows 系统中为 **idf.espRainmakerPathWin**)。 + - 克隆 ESP-Rainmaker,并配置 **RMAKER_PATH** 在 **idf.customExtraVars**。 * - 安装 ESP-HomeKit-SDK - - 在所选目录中克隆 ESP-HomeKit-SDK,并配置 **idf.espHomeKitSdkPath**(Windows 系统中为 **idf.espHomeKitSdkPathWin**)。 + - 在所选目录中克隆 ESP-HomeKit-SDK,并配置 **HOMEKIT_PATH** 在 **idf.customExtraVars**。 * - 启动 IDF 监视器以支持 Core Dump 模式/GDB Stub 模式 - 启动支持 WebSocket 的 ESP-IDF 监控器。如果紧急处理程序已经配置为 gdbstub 或核心转储,监控器将启动芯片的事后调试会话。 * - 启动 QEMU 服务器 @@ -127,7 +127,7 @@ * - 设置 ESP-MATTER 设备路径 (ESP_MATTER_DEVICE_PATH) - **ESP-IDF:设置 ESP-MATTER 设备路径 (ESP_MATTER_DEVICE_PATH)** 命令用于定义 ESP-Matter 的设备路径。Windows 系统不支持 ESP-Matter。 * - 展示示例项目 - - 启动 UI 以显示所选框架的示例,可从中创建新项目。此命令将显示扩展中已配置的框架,如果想查看 ESP-Rainmaker 示例,需要先运行 **安装 ESP-Rainmaker** 命令(或设置相应的 idf.espRainmakerPath),然后执行此命令以查看示例。 + - 启动 UI 以显示所选框架的示例,可从中创建新项目。此命令将显示扩展中已配置的框架,如果想查看 ESP-Rainmaker 示例,需要先运行 **安装 ESP-Rainmaker** 命令(或设置相应的 RMAKER_PATH 在 idf.customExtraVars),然后执行此命令以查看示例。 * - 显示 Ninja 构建摘要 - 运行 Chromium ninja-build-summary.py。 * - 二进制文件大小分析 diff --git a/docs_espressif/zh_CN/configureproject.rst b/docs_espressif/zh_CN/configureproject.rst index eeb165442..421f6a8d9 100644 --- a/docs_espressif/zh_CN/configureproject.rst +++ b/docs_espressif/zh_CN/configureproject.rst @@ -45,14 +45,12 @@ LLVM CLangd 扩展只需要用到 ``compile_commands.json`` 文件;而 Microso "compilerPath": "/path/to/toolchain-gcc", "compileCommands": "${workspaceFolder}/build/compile_commands.json", "includePath": [ - "${config:idf.espIdfPath}/components", - "${config:idf.espIdfPathWin}/components/", + "/path/to/esp-idf/components/**", "${workspaceFolder}/" ], "browse": { "path": [ - "${config:idf.espIdfPath}/components", - "${config:idf.espIdfPathWin}/components", + "/path/to/esp-idf/components/**", "${workspaceFolder}" ] } diff --git a/docs_espressif/zh_CN/settings.rst b/docs_espressif/zh_CN/settings.rst index a397b9a39..d4d4adf97 100644 --- a/docs_espressif/zh_CN/settings.rst +++ b/docs_espressif/zh_CN/settings.rst @@ -13,7 +13,7 @@ ESP-IDF 设置 .. note:: - 请注意,配置此扩展时,``~``、``%VARNAME%`` 和 ``$VARNAME`` 都无法被识别。请使用 ``${env:VARNAME}`` 来设置路径中的环境变量,例如 ``${env:HOME}``。也可以通过 ``${config:SETTINGID}`` 来引用其他配置参数,例如 ``${config:idf.espIdfPath}``。 + 请注意,配置此扩展时,``~``、``%VARNAME%`` 和 ``$VARNAME`` 都无法被识别。请使用 ``${env:VARNAME}`` 来设置路径中的环境变量,例如 ``${env:HOME}``。也可以通过 ``${config:SETTINGID}`` 来引用其他配置参数,例如 ``${config:idf.buildPath}``。 在运行 **设置乐鑫设备目标** 等命令时,**idf.saveScope** 可指定保存设置的位置。可选择将设置保存在全局(用户设置)、工作区或工作区文件夹。请使用 **选选择配置存储位置** 命令来选择保存设置的位置。 @@ -52,26 +52,13 @@ ESP-IDF 相关设置 - 在构建任务中启用 CCache(确保 CCache 在 PATH 中) * - idf.enableIdfComponentManager - 在构建命令中启用 IDF 组件管理器 - * - idf.espIdfPath - - ESP-IDF 框架的位置路径 (IDF_PATH) - * - idf.espIdfPathWin - - Windows 系统中 ESP-IDF 框架的位置路径 (IDF_PATH) * - idf.ninjaArgs - Ninja 构建任务的参数 - * - idf.pythonInstallPath - - 用于构建 ESP-IDF Python 虚拟环境的系统 Python 绝对路径 - * - idf.toolsPath - - ESP-IDF 工具的位置路径 (IDF_TOOLS_PATH) - * - idf.toolsPathWin - - Windows 系统中 ESP-IDF 工具的位置路径 (IDF_TOOLS_PATH) 扩展将按照以下方式使用上述设置: 1. **idf.customExtraVars** 用于存储自定义环境变量,例如 OPENOCD_SCRIPTS,用于指定启动 OpenOCD 服务器时所需脚本文件的目录路径。这些变量会加载到扩展命令的进程环境变量中,优先使用扩展变量,如果没有,则扩展命令会尝试使用系统 PATH 中已有的设置。**该配置项不会改变 VS Code 之外的系统环境。** -2. **idf.espIdfPath**(Windows 系统中为 **idf.espIdfPathWin**)用于在扩展中存储 ESP-IDF 目录路径。如果该值已配置,则 VS Code 进程中原有的 IDF_PATH 会被覆盖。**该配置项不会改变 VS Code 之外的系统环境。** 此外,扩展使用 **idf.espIdfPath**,结合 **idf.toolsPath** 和 **idf.pythonInstallPath**,来确定要添加到环境变量 PATH 中的 ESP-IDF 工具路径和 Python 虚拟环境路径。 -3. **idf.pythonInstallPath** 是系统 Python 的绝对路径,基于 **idf.toolsPath** 和 **idf.espIdfPath** 来生成 ESP-IDF Python 虚拟环境路径。创建虚拟环境后,ESP-IDF 的 Python 包将在该环境中安装和使用。 -4. **idf.gitPath**(Windows 系统中为 **idf.gitPathWin**)在扩展中用于克隆 ESP-IDF master 版本及其他支持的框架,如 ESP-ADF、ESP-MDF 和 Arduino-ESP32。 -5. **idf.toolsPath**(Windows 系统中为 **idf.toolsPathWin**)用于结合 **idf.toolsPath** 和 **idf.pythonInstallPath** 来确定要添加到环境变量 PATH 中的 ESP-IDF 工具路径和 Python 虚拟环境路径。 +2. **idf.gitPath**(Windows 系统中为 **idf.gitPathWin**)在扩展中用于克隆 ESP-IDF master 版本及其他支持的框架,如 ESP-ADF、ESP-MDF 和 Arduino-ESP32。 .. note:: @@ -294,20 +281,6 @@ QEMU 相关设置 * - 设置 ID - 描述 - * - **idf.espAdfPath** - - 定位 ESP-ADF 框架的路径 (ADF_PATH) - * - **idf.espAdfPathWin** - - 在 Windows 系统中定位 ESP-ADF 框架的路径 (ADF_PATH) - * - **idf.espMdfPath** - - 定位 ESP-MDF 框架的路径 (MDF_PATH) - * - **idf.espMdfPathWin** - - 在 Windows 系统中定位 ESP-MDF 框架的路径 (MDF_PATH) - * - **idf.espMatterPath** - - 定位 ESP-Matter 框架的路径 (ESP_MATTER_PATH) - * - **idf.espRainmakerPath** - - 定位 ESP-Rainmaker 框架的路径 (RMAKER_PATH) - * - **idf.espRainmakerPathWin** - - 在 Windows 系统中定位 ESP-Rainmaker 框架的路径 (RMAKER_PATH) * - **idf.sbomFilePath** - 创建 ESP-IDF SBOM 报告的路径 @@ -317,4 +290,4 @@ QEMU 相关设置 环境变量 (env) 和其他 ESP-IDF 设置 (config) 可以在 ESP-IDF 设置中通过 ``${env:VARNAME}`` (用于环境变量)和 ``${config:ESPIDFSETTING}`` (用于设置)进行引用。 -例如,如果想要使用 ``"~/esp/esp-idf"``,可以将 **idf.espIdfPath** 设为 ``"${env:HOME}/esp/esp-idf"``。 +例如,如果想要使用 ``"~/esp/esp-idf"`` 设为 ``"${env:HOME}/esp/esp-idf"``。 diff --git a/export.bat b/export.bat deleted file mode 100644 index 9b6fea503..000000000 --- a/export.bat +++ /dev/null @@ -1,7 +0,0 @@ -DOSKEY idf.py=python.exe "%IDF_PATH%\tools\idf.py" $* -DOSKEY esptool.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\esptool.py" $* -DOSKEY espefuse.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espefuse.py" $* -DOSKEY espsecure.py=python.exe "%IDF_PATH%\components\esptool_py\esptool\espsecure.py" $* -DOSKEY otatool.py=python.exe "%IDF_PATH%\components\app_update\otatool.py" $* -DOSKEY parttool.py=python.exe "%IDF_PATH%\components\partition_table\parttool.py" $* -cls \ No newline at end of file diff --git a/l10n/bundle.l10n.es.json b/l10n/bundle.l10n.es.json index 905d096c4..95b2f2ef4 100644 --- a/l10n/bundle.l10n.es.json +++ b/l10n/bundle.l10n.es.json @@ -48,9 +48,9 @@ "ESP-IDF: SDK Configuration Editor": "ESP-IDF: Editor de configuración del SDK", "ESP-IDF: Save Default Configuration (save-defconfig)": "ESP-IDF: Guardar configuración predeterminada (save-defconfig)", "Enter target name (IDF_TARGET)": "Introduzca el nombre del objetivo (IDF_TARGET)", - "ESP-IDF: Configure ESP-IDF extension": "ESP-IDF: Configurar la extensión ESP-IDF", "No ESP-IDF frameworks found": "No se encontraron marcos ESP-IDF", "Select framework to use": "Seleccione el marco a utilizar", + "Select mirror to use": "Seleccione el enlace a utilizar", "No framework selected to load examples.": "No se seleccionó ningún marco para cargar ejemplos.", "ESP-IDF: Loading examples": "ESP-IDF: Cargando ejemplos", "Cannot call this command directly, right click on any CMakeLists.txt file!": "No se puede llamar a este comando directamente, haga clic derecho en cualquier archivo CMakeLists.txt.", @@ -70,7 +70,7 @@ "Flash binary to this partition": "Flash binario a esta partición", "Open partition table editor": "Abrir editor de tablas de particiones", "Select an action to use": "Seleccione una acción para usar", - "ESP-IDF: Preparing ESP-IDF extension report": "ESP-IDF: Preparando informe de extensión ESP-IDF", + "ESP-IDF Doctor": "ESP-IDF Doctor", "Extension configuration report has been copied to clipboard with errors": "El informe de configuración de la extensión se ha copiado al portapapeles con errores", "No launch.json found.\nUse the 'ESP-IDF: Add vscode Configuration Folder' command.": "No se encontró launch.json.\n", "No gdbtarget configuration found in launch.json.\nDelete launch.json and use the 'ESP-IDF: Add vscode Configuration Folder' command.": "No se encontró ninguna configuración de gdbtarget en launch.json.\n", @@ -151,13 +151,17 @@ "Discarded changes in SDK Configuration editor": "Cambios descartados en el editor de configuración del SDK", "Error encountered while adding dependency {dependency} to the component \"{component}\"": "Se encontró un error al agregar la dependencia {dependency} al componente \"{component}\"", "Error encountered while creating project from example \"{example}\"": "Se encontró un error al crear un proyecto a partir del ejemplo \"{example}\"", + "Open ESP-IDF installation manager": "Abra el administrador de instalación ESP-IDF", + "Choose from existing ESP-IDF setups.": "Elija entre configuraciones ESP-IDF existentes.", + "The extension configuration is not valid. Choose an action:": "La configuración de la extensión no es válida. Elija una opción", + "No ESP-IDF Setups found": "No se encontraron configuraciones ESP-IDF", + "Use docker container configuration": "Usar la configuración del contenedor docker", "Install ESP-ADF": "Instalar ESP-ADF", "Install ESP-MDF": "Instalar ESP-MDF", "Install ESP-Matter": "Instalar ESP-Materia", "Install ESP-Rainmaker": "Instalar ESP-Rainmaker", "Project Configuration editor": "Editor de configuración del proyecto", "Select Project Configuration": "Seleccione Configuración del proyecto", - "Install Extension Python Requirements": "Instalar la extensión Requisitos de Python", "Install ESP-Matter Python Requirements": "Instalar los requisitos de ESP-Matter Python", "Add .vscode subdirectory files": "Agregar archivos de subdirectorio .vscode", "Add .devcontainer subdirectory files": "Agregar archivos de subdirectorio .devcontainer", @@ -166,7 +170,6 @@ "Flash with DFU": "Flashear con DFU", "Launch Websocket server and IDF Monitor": "Inicie el servidor Websocket y el monitor IDF", "Start/Stop QEMU Server": "Iniciar/detener el servidor QEMU", - "Configure ESP-IDF Extension": "Configurar la extensión ESP-IDF", "esp-clang not found in PATH. Make sure esp-clang is installed.": "No se ha encontrado a esp-clang en PATH. Verifique la instalación de esp-clang.", "Select current ESP-IDF version": "Seleccione la versión actual de ESP-IDF", "New Project Wizard": "Asistente para nuevos proyectos", diff --git a/l10n/bundle.l10n.pt.json b/l10n/bundle.l10n.pt.json index 57fd05a21..8628558cf 100644 --- a/l10n/bundle.l10n.pt.json +++ b/l10n/bundle.l10n.pt.json @@ -48,9 +48,9 @@ "ESP-IDF: SDK Configuration Editor": "ESP-IDF: Editor de configuração do SDK", "ESP-IDF: Save Default Configuration (save-defconfig)": "ESP-IDF: Salvar configuração padrão (save-defconfig)", "Enter target name (IDF_TARGET)": "Insira o nome do destino (IDF_TARGET)", - "ESP-IDF: Configure ESP-IDF extension": "ESP-IDF: Configurar extensão ESP-IDF", "No ESP-IDF frameworks found": "Nenhuma estrutura ESP-IDF encontrada", "Select framework to use": "Selecione a estrutura a ser usada", + "Select mirror to use": "Selecione o espelho a usar", "No framework selected to load examples.": "Nenhuma estrutura selecionada para carregar exemplos.", "ESP-IDF: Loading examples": "ESP-IDF: Carregando exemplos", "Cannot call this command directly, right click on any CMakeLists.txt file!": "Não é possível chamar este comando diretamente, clique com o botão direito em qualquer arquivo CMakeLists.txt!", @@ -70,7 +70,7 @@ "Flash binary to this partition": "Flash binário para esta partição", "Open partition table editor": "Abra o editor de tabela de partição", "Select an action to use": "Selecione uma ação para usar", - "ESP-IDF: Preparing ESP-IDF extension report": "ESP-IDF: Preparando relatório de extensão ESP-IDF", + "ESP-IDF Doctor": "ESP-IDF Doctor", "Extension configuration report has been copied to clipboard with errors": "O relatório de configuração da extensão foi copiado para a área de transferência com erros", "No launch.json found.\nUse the 'ESP-IDF: Add vscode Configuration Folder' command.": "Nenhum launch.json encontrado.\n", "No gdbtarget configuration found in launch.json.\nDelete launch.json and use the 'ESP-IDF: Add vscode Configuration Folder' command.": "Nenhuma configuração gdbtarget encontrada em launch.json.\n", @@ -151,13 +151,17 @@ "Discarded changes in SDK Configuration editor": "Alterações descartadas no editor de configuração do SDK", "Error encountered while adding dependency {dependency} to the component \"{component}\"": "Erro encontrado ao adicionar a dependência {dependency} ao componente \"{component}\"", "Error encountered while creating project from example \"{example}\"": "Erro encontrado ao criar o projeto do exemplo \"{example}\"", + "Open ESP-IDF installation manager": "Abra o gerenciador de instalação ESP-IDF", + "Choose from existing ESP-IDF setups.": "Escolha entre as configurações ESP-IDF existentes.", + "The extension configuration is not valid. Choose an action:": "A configuração da extensão não é válida. Escolha uma ação:", + "No ESP-IDF Setups found": "Nenhuma configuração ESP-IDF encontrada", + "Use docker container configuration": "Use a configuração do contêiner do Docker", "Install ESP-ADF": "Instale ESP-ADF", "Install ESP-MDF": "Instale ESP-MDF", "Install ESP-Matter": "Instale ESP-Matter", "Install ESP-Rainmaker": "Instale ESP-Rainmaker", "Project Configuration editor": "Editor de configuração do projeto", "Select Project Configuration": "Selecione Configuração do Projeto", - "Install Extension Python Requirements": "Instalar requisitos de extensão Python", "Install ESP-Matter Python Requirements": "Instale os requisitos do ESP-Matter Python", "Add .vscode subdirectory files": "Adicionar arquivos de subdiretório .vscode", "Add .devcontainer subdirectory files": "Adicionar arquivos de subdiretório .devcontainer", @@ -168,7 +172,6 @@ "Launch Websocket server and IDF Monitor": "Inicie o servidor Websocket e o IDF Monitor", "Start/Stop QEMU Server": "Iniciar/parar servidor QEMU", "esp-clang not found in PATH. Make sure esp-clang is installed.": "esp-clang não encontrado em PATH. Certifique-se de que o esp-clang está instalado.", - "Configure ESP-IDF Extension": "Configurar extensão ESP-IDF", "Select current ESP-IDF version": "Selecione a versão atual do ESP-IDF", "New Project Wizard": "Assistente de Novo Projeto", "Select Current Project workspace folder": "Selecione a pasta do espaço de trabalho do Projeto Atual", diff --git a/l10n/bundle.l10n.ru.json b/l10n/bundle.l10n.ru.json index 814117a41..57ee73e2c 100644 --- a/l10n/bundle.l10n.ru.json +++ b/l10n/bundle.l10n.ru.json @@ -48,9 +48,9 @@ "ESP-IDF: SDK Configuration Editor": "ESP-IDF: Редактор конфигурации SDK", "ESP-IDF: Save Default Configuration (save-defconfig)": "ESP-IDF: сохранить конфигурацию по умолчанию (save-defconfig)", "Enter target name (IDF_TARGET)": "Введите имя цели (IDF_TARGET)", - "ESP-IDF: Configure ESP-IDF extension": "ESP-IDF: настройка расширения ESP-IDF", "No ESP-IDF frameworks found": "Фреймворки ESP-IDF не найдены", "Select framework to use": "Выберите платформу для использования", + "Select mirror to use": "Выберите зеркало для использования", "No framework selected to load examples.": "Для загрузки примеров не выбрана платформа.", "ESP-IDF: Loading examples": "ESP-IDF: Загрузка примеров", "Cannot call this command directly, right click on any CMakeLists.txt file!": "Невозможно вызвать эту команду напрямую. Кликните правой кнопкой мыши любой файл CMakeLists.txt!", @@ -70,7 +70,7 @@ "Flash binary to this partition": "Записать двоичный файл в этот раздел", "Open partition table editor": "Открыть редактор таблицы разделов", "Select an action to use": "Выбор действия для использования", - "ESP-IDF: Preparing ESP-IDF extension report": "ESP-IDF: подготовка отчета о расширении ESP-IDF", + "ESP-IDF Doctor": "ESP-IDF Доктор", "Extension configuration report has been copied to clipboard with errors": "Отчет о конфигурации расширения скопирован в буфер обмена с ошибками", "No launch.json found.\nUse the 'ESP-IDF: Add vscode Configuration Folder' command.": "Файл launch.json не найден.\nИспользуйте команду 'ESP-IDF: Add vscode Configuration Folder'.", "No gdbtarget configuration found in launch.json.\nDelete launch.json and use the 'ESP-IDF: Add vscode Configuration Folder' command.": "В файле launch.json не найдена конфигурация gdbtarget.\nУдалите файл launch.json и используйте команду 'ESP-IDF: Add vscode Configuration Folder'.", @@ -155,6 +155,11 @@ "Install ESP-MDF": "Установка ESP-MDF", "Install ESP-Matter": "Установка ESP-Matter", "Install ESP-Rainmaker": "Установка ESP-Rainmaker", + "Open ESP-IDF installation manager": "Откройте диспетчер установки ESP-IDF.", + "Choose from existing ESP-IDF setups.": "Выбирайте из существующих настроек ESP-IDF.", + "The extension configuration is not valid. Choose an action:": "Недопустимая конфигурация расширения. Выберите действие:", + "No ESP-IDF Setups found": "Настройки ESP-IDF не найдены", + "Use docker container configuration": "Использовать конфигурацию Docker-контейнера", "Project Configuration editor": "Редактор конфигурации проекта", "Select Project Configuration": "Выбор конфигурации проекта", "Install Extension Python Requirements": "Установка требований Python расширения", @@ -168,7 +173,6 @@ "Launch Websocket server and IDF Monitor": "Запуск сервера Websocket и монитора IDF", "Start/Stop QEMU Server": "Запуск/остановка сервера QEMU", "esp-clang not found in PATH. Make sure esp-clang is installed.": "esp-clang не найден в PATH. Убедитесь, что esp-clang установлен.", - "Configure ESP-IDF Extension": "Настройка расширения ESP-IDF", "Select current ESP-IDF version": "Выбор текущей версии ESP-IDF", "New Project Wizard": "Мастер создания нового проекта", "Select Current Project workspace folder": "Выбор папки рабочей области текущего проекта", diff --git a/l10n/bundle.l10n.zh-CN.json b/l10n/bundle.l10n.zh-CN.json index cd6454036..ceea12597 100644 --- a/l10n/bundle.l10n.zh-CN.json +++ b/l10n/bundle.l10n.zh-CN.json @@ -48,9 +48,9 @@ "ESP-IDF: SDK Configuration Editor": "ESP-IDF:SDK 配置编辑器", "ESP-IDF: Save Default Configuration (save-defconfig)": "ESP-IDF:保存默认配置 (save-defconfig)", "Enter target name (IDF_TARGET)": "输入目标名称 (IDF_TARGET)", - "ESP-IDF: Configure ESP-IDF extension": "ESP-IDF:配置 ESP-IDF 扩展", "No ESP-IDF frameworks found": "未找到 ESP-IDF 框架", "Select framework to use": "选择要使用的框架", + "Select mirror to use": "选择要使用的镜像", "No framework selected to load examples.": "未选择任何框架来加载示例。", "ESP-IDF: Loading examples": "ESP-IDF:加载示例", "Cannot call this command directly, right click on any CMakeLists.txt file!": "不能直接调用此命令,需右键单击任意一个 CMakeLists.txt 文件!", @@ -70,7 +70,7 @@ "Flash binary to this partition": "将二进制文件烧录到该分区", "Open partition table editor": "打开分区表编辑器", "Select an action to use": "选择要执行的操作", - "ESP-IDF: Preparing ESP-IDF extension report": "ESP-IDF:正在生成 ESP-IDF 扩展报告", + "ESP-IDF Doctor": "ESP-IDF 诊断工具", "Extension configuration report has been copied to clipboard with errors": "已复制扩展配置报告到剪贴板,但有错误", "No launch.json found.\nUse the 'ESP-IDF: Add vscode Configuration Folder' command.": "未找到 launch.json。\n运行命令 ESP-IDF:添加 VS Code 配置文件夹", "No gdbtarget configuration found in launch.json.\nDelete launch.json and use the 'ESP-IDF: Add vscode Configuration Folder' command.": "launch.json 中未找到 gdbtarget 配置。\n删除 launch.json 并运行命令 ESP-IDF:添加 VS Code 配置文件夹。", @@ -151,13 +151,17 @@ "Discarded changes in SDK Configuration editor": "已放弃 SDK 配置编辑器中的更改", "Error encountered while adding dependency {dependency} to the component \"{component}\"": "将依赖项 {dependency} 添加到组件 \"{component}\" 时出错", "Error encountered while creating project from example \"{example}\"": "基于 \"{example}\" 示例创建项目时出错", + "Open ESP-IDF installation manager": "打开 ESP-IDF 安装管理器", + "Choose from existing ESP-IDF setups.": "从现有 ESP-IDF 设置中进行选择。", + "The extension configuration is not valid. Choose an action:": "扩展配置无效 选择一个操作:", + "No ESP-IDF Setups found": "未找到 ESP-IDF 设置", + "Use docker container configuration": "使用docker容器配置", "Install ESP-ADF": "安装 ESP-ADF", "Install ESP-MDF": "安装 ESP-MDF", "Install ESP-Matter": "安装 ESP-Matter", "Install ESP-Rainmaker": "安装 ESP-Rainmaker", "Project Configuration editor": "项目配置编辑器", "Select Project Configuration": "选择项目配置", - "Install Extension Python Requirements": "为扩展安装 Python 依赖包", "Install ESP-Matter Python Requirements": "为 ESP-Matter 安装 Python 依赖包", "Add .vscode subdirectory files": "添加 .vscode 子目录文件", "Add .devcontainer subdirectory files": "添加 .devcontainer 子目录文件", @@ -168,7 +172,6 @@ "Launch Websocket server and IDF Monitor": "启动 WebSocket 服务器和 IDF 监视器", "Start/Stop QEMU Server": "启动/停止 QEMU 服务器", "esp-clang not found in PATH. Make sure esp-clang is installed.": "在 PATH 中未找到 esp-clang。请确保已安装 esp-clang。", - "Configure ESP-IDF Extension": "配置 ESP-IDF 扩展", "Select current ESP-IDF version": "选择当前的 ESP-IDF 版本", "New Project Wizard": "新项目向导", "Select Current Project workspace folder": "选择当前项目的工作区文件夹", diff --git a/media/walkthrough/express-setup.png b/media/walkthrough/express-setup.png deleted file mode 100644 index a73abfef3d2b963624811afe5f162987a7603ebc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15009 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 diff --git a/media/walkthrough/idf-version.png b/media/walkthrough/idf-version.png deleted file mode 100644 index ea07af0087f6a3fb6a324676ad4adf0a7096fa7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10843 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
}o!$ z86ssmXOpO~48W&TR8ZX+&1iw&pAtONT^kDU(Y~Z zo#jwM&ur(aa&(>KkaDsNysBlB7`2Sg*POl12k3@x=c+eJnI4giPVNs4Oolb(*>Cj7!Lj!d|GZBZ@~mgnuA58f4jFIrgjz69<8x~$*dG9!f@Ny@ z_Mr4Bu0pScxT~M*d_v|T{q}FWTz}R^Z#c_q-SCs*vZG^S!XG1Fl*uPE_hFC-do`x>eqx7sc#5U9d*7uDgmA-EPnYXx z3??$rZT7X9$Ke{f9aYPGSIQADNEZ*rd$xXvU?`S%A6qE8K=^T-C@9Z#AUt34%LW{I zb~yt_KeY^&{P*`Szke)Mud!d|_aE29VRR8DLPgKQ1c}ht4Ph0#A8NO_mNV)#!|E`* zhD+|xy0J%J>Dg3C4+RxXi0jl@+E=Bs8U^rS5npC^l5`o`vU%QCeG#pRK=&swUHb{= z1UQPci(IL2*~NN0(ySu{WOP;#`VtW)F=tFDxsN34A;ZTK(-Vm(6q?J+sSl~1b^BWb z@zNa95)Zo~PgY@LdUVX8!0~v|_jI-B;OrGuYBpOzKHRpONn+#`8S`{B@2o)fmyRFj zECV(kGA?L6UGfIl+#D^GJ%RCQQoYU$ysMOT&{gkQHo3yoUO~6BsgUeum!{FB2CL&( zvc?ZtEf`vc8*KZ4j*l%h$K?8>$jHubx{k&XkTE#XE#_-jwi7k<+0qamnd>k}k6^Qv zt3u~9Iu23W|4itA#mVJ&)f5t zL}~FUU%0Ep$A3EHD4;ZW{IND)Z|1_`oJ~kjgDlJlKu=`Eak`i+g`lg;8ed;Bn9KVA zHbpSP7jW7NGkNb2$57cii9`Hte(D2uQg!6iN}T~tiNDfl$=1vADEp|y3plaX zYWH&Sv_nXQx`w246wZ6&Mw=Jw_cy=Qp46P2*pv4W%ZJ-vcNrVfNgr*Mek?7>hKMr$ z8cyXH>B`r8y6G1g?g_Q6p@M}=?@JZI0oTR1fJ4pzi=!Nsn6pO02RTZA2YvgBv8nE^F_rP5u9-agF1;Jo9tW_d=3F?u-TZKq3wBXEHP z!M4kE&HYp=NeFmU;^lr=&uRd65b6j##b4x!wTqv-YK>==rlL5V48ZaFy}~~Gu|y4P z$)eNq`}@RqA)?3L3gisD{cTuIcZ_k=1?#6i*N$oVOU^^UZV$pHe$(EnUatKf@Pd(6 z5Rl>4ua4$;66aUGxi5dAVZxHaSt6wNlZZIX8(FA@D^8oT^}ZI8LANn|EoO~k>bE9RlG+J;A{$k6;mnp`q zV9Rio3N@PBU60zThQo5;=OnLFMl_}(n~)T~UYZ(14ROJ`iCl6Qx!qF5f9SbftT8J+ zT5K45m_x0K_)u8vE=K_g!68hsA4%il`p{s%oW}g})T4=PdmpdS9>;HQRiyVunDVt( zsx9~%Phm57oD=un&7tiNxyC3=RiTGU>MP=jXFoi<`^Fc#$SWy|V!8N&KD+U}{>YsS zflA-F`(ZAH0hRCXkq6B<44J9!t_yp)Vq`xUV*x8JcfVjzP>YosDMgqWreHT4DIybS z=V`~D6y?7W2Q$ik&Np#okHseAe-%d2tZcDP9fNNkz_lBeRNcsJzqIDn&LR9V{g>TB z9ct@Y_(j_6`<;=yxfrBvL;VTnb8{I@%^QRXw6ocDI!StxT$y@@Rr{zDkr*Mk03~+c z$K9OQQZ8A+k$i4b5S~=24+6=Ia??dr?+;ISCW z;wZ0`pwrpPEL~r3s0lLdn>WO&uc(4#%gXfI79?jh(Wt5%qCNL;l^Kfgf> zzuCE{J&NSOogpGBPF;0R56wjdKJJIDpZ_6KN!)XmCN)v(A?==w48fI*d4jsv&Dda6^d*TQ@@nj~PSKB`fnYj*Es zE4p?NWi3I$(tcxc0f}7raVT4;NCpwu6WOWbxmhdEvShCes2W%clyVekef)bP3hMn9 zI&#k9OJvm^+u0!DW`Px-0-Zx)H!JgO$c^XZ+0P?a%fQHSIg9F z8%r@>*rY>$>4q{5bu{(*Jr+V@;NU}5b~8DrQ1cE3*=nbV)>E$@I`rauZ|%O!S8f21 zD74q`zmY137EAz^6=nu-Dr0w-9}Ztd;Uq=n%0XDou3G;;UjMvisS7kRRv~(upsBmD z5zgCzqYP4>W@Z2wq7q{Isz%JkuHxW4NB`FFzJ%D6>2*P6KA13zM(V42!cCBYFNn1T z6L$Yh9R*Obw|e;QW464Myc9F%j$@Xr3IWdF9#yMV^lU4rJ# zpN@pP1#2b+KwY``{y|-FDxPBIRACLGa3wSWA?7G{r-bhLS0AyCSm8D z2!J8TnvbTx-MyWlmkGfpXYT#_l4JLt64U$dMmUS<@xl-PyLCm6%RS0J2h-?=TXw5J z95n-2B*5IjS~aF$0E~qA)dp4+yW~73GDbufL3^2&08q1#P2!yv{Q0%MoyP%ypXyCk`*;T0LdP6+Su_&cwxFdm`E9J zztmVgF-HqYBajV54xO24b$51udbp0Pmu~3*(iEHzfA{jDP3NkNCV`1F#-}xSll$D{ z)H$r`+qeFf&SR>AYcvC3g459)+jV)&Zd2svUm<&ex-t~imh2+(3>J#^s5($%++8T% z=EnB$momFSBlPQaqjt(4j^0)ixxo{T`aZX2@kjNG_V~lhVTexjS~dA>ozIkC505mB!jM0kjkU9EF-LC*_fRO~u6nD6Ke~0Xr8k6u2HQ^fCGZ zRAl(oJR-pNV*%PdRHU32L8M=*&SbS%Uk)qNnliivHXce)bU}DbJpeMGVlT<{6Vx**O&5 zKhy1%HcxpKl{lB9IpvDxgLg)+?N?g0&|bcy0kR?GhG{kHc5_vomd-#}WP@}!c+ToU z2ZB)X5gSm_jZ!Yz zd#BJ$4sEA@YvHs**Hk;zbaP}nTHbo|owc@1)Ywv<@#hEO+n$>mAQ02e>N-b4jKRF- zuhU-akhe45%Lk(qV&xAUpbD4qe?E96LLR<^W=>E1T`%RT{rZitk$FLncg~< zXR$DByHz7Y!Arq}`j+>>j0*SmDT|$m=|tq<8XXErZutAJj5}}$$SG@R;eQS*JGc>N z0RxDF4*;5~wziNd`u@fU_wPVR??xyA<$|HO&K$~wW`86J-Kx)B`6qJLkk~pNT^|%Y zNb2vU#!q>C4eGs24NJzd_CTKN9LUY2_^f)KwuMw#($PSCDWr7KY@07r-4}pt-X=7d zA{@+QeP)b5{8mnu5ucuTl6^sQP4VE%xADe!a@0uIZ4wBk$j9Fsy;iECDMdM?l`;7E z1e4+vc=sQ1UM-CMnKf{rJpv~EdOU~_DtH_|Eo%MFf>Hg+ZZJ-nOh+i)ZpjP`U zZ7maclmuYR9v2wz3Aiei0~tR3Opk-N93KdobuCYRzc|kP zEvlF)Xyxd3cie#BqvzEWh>Xdu?Ak{~hZw-$Y*D6ZhiU9=W2-MtZReBdC1I^VO53hO zk)xE~C;5p*&6S^%%Z50&zT=w;z8JD zFQy7r3w2h1(llW;X;avkpL&>D|F*hIw`%N2=zc)GOs6~2pG#vx-}x)7h@4!D)p#?N zi;s^E;`66)q8(Ua(TZzwo)OU(o-_+C8ZhQ*lo(h+zo9B@UW)_I)d}_(foKC^>c{@3~+u52+^fCzz_Obbv;-2e=#(~ugTDs9d+;1(J_llNvT&po@ zB#lRz`1>X*Renod=ru>K^SEFR^dPCjOg9|LQ}3J{QRdc6aRJTYb(AQG1TTUEeyU4Gi{zzPt)TV(0 zKaHKx&2ty4Pkq4qeEC;B_#Y6bD>PjgL8ZY`+6CnMVerQ0$etae(dQgG$i&2_V{FI~ zpvr@1A9 zmLV{tM8o99gDkEj$oDjS2<2_-GbY3>>v(K*vW~5Uh4Eg#(Zy;qno%!O5^HunPRpUr zV#f9Faydt9$5vn=Z6V`#VWp)_dv!e?XWzU}dtu%&+j^E1zib1&3sW@yD~@L=)9MpT zjtF__iBK43i{S*d#8eq4-9v@h;t8>4dL(9O>D5Za)Mk7c1qA1`Wl3Gru&)2;dH=U6j5V z9wGg^X;1!OELQD!hHnOIAhlL$Hnw`Pfc=UMY){G0Z#GC!%f`x`avlT1RhRd;9dnL=V~ zW>eTo#QBt~E{=Y`FU@W112f8Oq4oo0)a%^0ptL{Q{;Yh6t+coa*H~;TsOpz^n^PSA zA1V3No7kHI(1i3|+pLn^wX}NM{?9p|-I+Va+F2ZnswNyOPl;`4L$|!8U^oqsp%fZ> z4UZH6t9cQ-Gs*~`5&>jSS%n$&4F*o}?2boH-F80TCBV)-5RI!I0URfDBN;BK zfY(93Z@w$@pBI>8s8QIfu*KMfW!sdjT=mS3LY+y5gOPK)_gan-?8iYE&-dPP MQi_rl;)VhL2eKG(XaE2J diff --git a/media/walkthrough/install-btn.png b/media/walkthrough/install-btn.png deleted file mode 100644 index 24bac1a41a1ec572541e9e1d864e39e2115d9911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4927 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 VlKx`n^!M4WbyHWPNd11`e*m!Y=zjnJ diff --git a/media/walkthrough/python-selection.png b/media/walkthrough/python-selection.png deleted file mode 100644 index 4874457222ff6119532f964dd8aae5909e1e0204..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14441 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 ZQr-m&@@N8Wf8T { return { - [AdvancedCommandKeys.Setup]: { + [AdvancedCommandKeys.InstallManager]: { checkboxState: undefined, - iconId: "extensions", - tooltip: l10n.t("Configure ESP-IDF Extension"), + iconId: "link-external", + tooltip: l10n.t("Open ESP-IDF Installation Manager"), }, [AdvancedCommandKeys.NewProject]: { checkboxState: undefined, @@ -130,11 +129,6 @@ export function createAdvancedCommandDictionary(): Record< iconId: "project", tooltip: l10n.t("Project Configuration editor"), }, - [AdvancedCommandKeys.InstallIdfPythonReqs]: { - checkboxState: undefined, - iconId: "extensions", - tooltip: l10n.t("Install Extension Python Requirements"), - }, [AdvancedCommandKeys.InstallMatterPythonReqs]: { checkboxState: undefined, iconId: "extensions", diff --git a/src/common/abstractCloning.ts b/src/common/abstractCloning.ts index 38a1c1b7d..40896058b 100644 --- a/src/common/abstractCloning.ts +++ b/src/common/abstractCloning.ts @@ -91,7 +91,14 @@ export class AbstractCloning { workspace?: Uri, recursiveDownload?: boolean ) { - const toolsDir = await idfConf.readParameter("idf.toolsPath", workspace); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const customExtraVars = idfConf.readParameter( + "idf.customExtraVars", + workspace + ) as { [key: string]: string }; + const toolsDir = currentEnvVars["IDF_TOOLS_PATH"]; const installDir = await window.showQuickPick( [ { @@ -150,7 +157,12 @@ export class AbstractCloning { if (installDir.target === "existing") { const target = idfConf.readParameter("idf.saveScope"); - await idfConf.writeParameter(configurationId, installDirPath, target); + customExtraVars[configurationId] = installDirPath; + await idfConf.writeParameter( + "idf.customExtraVars", + customExtraVars, + target + ); Logger.infoNotify(`${this.name} has been installed`); return; } @@ -204,11 +216,20 @@ export class AbstractCloning { await this.updateSubmodules(resultingPath, undefined, progress); } const target = idfConf.readParameter("idf.saveScope"); - await idfConf.writeParameter(configurationId, resultingPath, target); + customExtraVars[configurationId] = installDirPath; + await idfConf.writeParameter( + "idf.customExtraVars", + customExtraVars, + target + ); Logger.infoNotify(`${this.name} has been installed`); } catch (error) { OutputChannel.appendLine(error.message); - Logger.errorNotify(error.message, error, "AbstractCloning getRepository"); + Logger.errorNotify( + error.message, + error, + "AbstractCloning getRepository" + ); } } ); @@ -377,7 +398,11 @@ export class AbstractCloning { if (!signal && code !== 0) { const msg = `Submodules clone has exit with ${code}`; OutputChannel.appendLine(msg); - Logger.errorNotify("Submodules cloning error", new Error(msg), "AbstractCloning spawnWithProgress"); + Logger.errorNotify( + "Submodules cloning error", + new Error(msg), + "AbstractCloning spawnWithProgress" + ); return reject(new Error(msg)); } return resolve(); @@ -414,7 +439,11 @@ export class AbstractCloning { Logger.infoNotify(`${repoName} submodules checked out successfully`); } catch (error) { OutputChannel.appendLine(error.message); - Logger.errorNotify(error.message, error, "AbstractCloning getSubmodules"); + Logger.errorNotify( + error.message, + error, + "AbstractCloning getSubmodules" + ); } } ); diff --git a/src/common/prepareEnv.ts b/src/common/prepareEnv.ts new file mode 100644 index 000000000..2697963e8 --- /dev/null +++ b/src/common/prepareEnv.ts @@ -0,0 +1,254 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Tuesday, 25th February 2025 2:10:52 pm + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Uri } from "vscode"; +import { readParameter } from "../idfConfiguration"; +import { Logger } from "../logger/logger"; +import { delimiter, dirname, join } from "path"; +import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { ESP } from "../config"; +import { isBinInPath } from "../utils"; +import { pathExists } from "fs-extra"; + +/** + * Configures and prepares environment variables necessary for executing ESP-IDF tasks. + * + * Merges environment variables from the current process (`process.env`), + * stored project configuration, and relevant VS Code settings (e.g., `idf.customExtraVars`, + * `idf.gitPath`, `idf.sdkconfigFilePath`, `idf.enableIdfComponentManager`). + * + * Key actions include: + * - Setting default `IDF_PATH` and `IDF_TOOLS_PATH` if not already defined. + * - Prepending required toolchain, Python virtual environment, IDF Tools, Git + * and component directories to the system `PATH`. + * - Determining and setting `IDF_TARGET` based on the workspace's sdkconfig. + * - Setting the `IDF_COMPONENT_MANAGER` flag and `SDKCONFIG` path based on settings. + * + * @async + * @param {Uri} curWorkspace - The Uri of the current workspace, used to access settings and sdkconfig. + * @returns {Promise<{[key: string]: string}>} A promise resolving to the configured + * environment variables object, ready for use in ESP-IDF tasks. + */ +export async function configureEnvVariables( + curWorkspace: Uri +): Promise<{ [key: string]: string }> { + const modifiedEnv: { [key: string]: string } = <{ [key: string]: string }>( + Object.assign({}, process.env) + ); + + let pathNameInEnv: string = Object.keys(process.env).find( + (k) => k.toUpperCase() == "PATH" + ); + + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + + if (currentEnvVars) { + try { + for (const envVar in currentEnvVars) { + if (envVar && envVar.toUpperCase() !== "PATH") { + modifiedEnv[envVar] = currentEnvVars[envVar]; + } else if (envVar.toUpperCase() === "PATH") { + modifiedEnv[ + pathNameInEnv + ] = `${currentEnvVars[envVar]}${delimiter}${modifiedEnv[pathNameInEnv]}`; + } + } + } catch (error) { + Logger.errorNotify( + "Invalid project configuration environment variables format", + error, + "configureEnvVariables ProjectConfiguration.CURRENT_IDF_CONFIGURATION" + ); + } + } + + const customExtraVars = readParameter( + "idf.customExtraVars", + curWorkspace + ) as { [key: string]: string }; + if (customExtraVars) { + try { + for (const envVar in customExtraVars) { + if (envVar && envVar.toUpperCase() !== "PATH") { + modifiedEnv[envVar] = customExtraVars[envVar]; + } + } + } catch (error) { + Logger.errorNotify( + "Invalid user environment variables format", + error, + "configureEnvVariables idf.customExtraVars" + ); + } + } + + try { + const openOcdPath = await isBinInPath("openocd", modifiedEnv, [ + "openocd-esp32", + ]); + if (openOcdPath) { + const openOcdDir = dirname(openOcdPath); + const openOcdScriptsPath = join( + openOcdDir, + "..", + "share", + "openocd", + "scripts" + ); + const scriptsExists = await pathExists(openOcdScriptsPath); + if (scriptsExists && modifiedEnv.OPENOCD_SCRIPTS !== openOcdScriptsPath) { + modifiedEnv.OPENOCD_SCRIPTS = openOcdScriptsPath; + } + } + } catch (error) { + Logger.error( + `Error processing OPENOCD_SCRIPTS path: ${error.message}`, + error, + "configureEnvVariables OPENOCD_SCRIPTS" + ); + } + + const containerPath = + process.platform === "win32" ? modifiedEnv.USERPROFILE : modifiedEnv.HOME; + const defaultEspIdfPath = join(containerPath, "esp", "esp-idf"); + + modifiedEnv.IDF_PATH = modifiedEnv.IDF_PATH || defaultEspIdfPath; + + const defaultToolsPath = join(containerPath, ".espressif"); + modifiedEnv.IDF_TOOLS_PATH = modifiedEnv.IDF_TOOLS_PATH || defaultToolsPath; + + let pathToPigweed: string; + + if (modifiedEnv.ESP_MATTER_PATH) { + pathToPigweed = join( + modifiedEnv.ESP_MATTER_PATH, + "connectedhomeip", + "connectedhomeip", + ".environment", + "cipd", + "packages", + "pigweed" + ); + modifiedEnv.ZAP_INSTALL_PATH = join( + modifiedEnv.ESP_MATTER_PATH, + "connectedhomeip", + "connectedhomeip", + ".environment", + "cipd", + "packages", + "zap" + ); + } + + const gitPath = readParameter("idf.gitPath", curWorkspace) as string; + let pathToGitDir; + if (gitPath && gitPath !== "git") { + pathToGitDir = dirname(gitPath); + } + + if ( + pathToGitDir && + !modifiedEnv[pathNameInEnv].split(delimiter).includes(pathToGitDir) + ) { + modifiedEnv[pathNameInEnv] += delimiter + pathToGitDir; + } + if ( + pathToPigweed && + !modifiedEnv[pathNameInEnv].split(delimiter).includes(pathToPigweed) + ) { + modifiedEnv[pathNameInEnv] += delimiter + pathToPigweed; + } + if (currentEnvVars["IDF_PYTHON_ENV_PATH"]) { + const pyDir = process.platform === "win32" ? "Scripts" : "bin"; + const venvPyContainer = join(currentEnvVars["IDF_PYTHON_ENV_PATH"], pyDir); + if ( + modifiedEnv[pathNameInEnv] && + !modifiedEnv[pathNameInEnv].includes(venvPyContainer) + ) { + modifiedEnv[pathNameInEnv] = + venvPyContainer + delimiter + modifiedEnv[pathNameInEnv]; + } + } + if ( + modifiedEnv[pathNameInEnv] && + !modifiedEnv[pathNameInEnv].includes(join(modifiedEnv.IDF_PATH, "tools")) + ) { + modifiedEnv[pathNameInEnv] = + join(modifiedEnv.IDF_PATH, "tools") + + delimiter + + modifiedEnv[pathNameInEnv]; + } + + if ( + currentEnvVars[pathNameInEnv] && + currentEnvVars[pathNameInEnv].length > 0 + ) { + const extraPathsArray = currentEnvVars[pathNameInEnv].split(delimiter); + for (let extraPath of extraPathsArray) { + if ( + modifiedEnv[pathNameInEnv] && + !modifiedEnv[pathNameInEnv].includes(extraPath) + ) { + modifiedEnv[pathNameInEnv] = + extraPath + delimiter + modifiedEnv[pathNameInEnv]; + } + } + } + + let IDF_ADD_PATHS_EXTRAS = join( + modifiedEnv.IDF_PATH, + "components", + "espcoredump" + ); + IDF_ADD_PATHS_EXTRAS = `${IDF_ADD_PATHS_EXTRAS}${delimiter}${join( + modifiedEnv.IDF_PATH, + "components", + "partition_table" + )}`; + + modifiedEnv[ + pathNameInEnv + ] = `${IDF_ADD_PATHS_EXTRAS}${delimiter}${modifiedEnv[pathNameInEnv]}`; + + let idfTarget = await getIdfTargetFromSdkconfig(curWorkspace); + if (idfTarget) { + modifiedEnv.IDF_TARGET = + modifiedEnv.IDF_TARGET || idfTarget || process.env.IDF_TARGET; + } + + let enableComponentManager = readParameter( + "idf.enableIdfComponentManager", + curWorkspace + ) as boolean; + + if (enableComponentManager) { + modifiedEnv.IDF_COMPONENT_MANAGER = "1"; + } + + let sdkconfigFilePath = readParameter( + "idf.sdkconfigFilePath", + curWorkspace + ) as string; + if (sdkconfigFilePath) { + modifiedEnv.SDKCONFIG = sdkconfigFilePath; + } + + return modifiedEnv; +} diff --git a/src/common/store.ts b/src/common/store.ts index 7ab37a522..66bcd5b2d 100644 --- a/src/common/store.ts +++ b/src/common/store.ts @@ -17,7 +17,6 @@ */ import { ExtensionContext } from "vscode"; -import { ESP } from "../config"; export class ExtensionConfigStore { private static self: ExtensionConfigStore; @@ -41,26 +40,4 @@ export class ExtensionConfigStore { public clear(key: string) { return this.set(key, undefined); } - - public clearIdfSetup(key: string) { - this.clear(key); - let currSetups = this.getIdfSetupKeys(); - const idfSetupIndex = currSetups.findIndex((s) => s === key); - if (idfSetupIndex === -1) { - return; - } - currSetups.splice(idfSetupIndex, 1); - this.updateIdfSetupKeys(currSetups); - } - - public getIdfSetupKeys() { - return this.ctx.globalState.get( - ESP.GlobalConfiguration.IDF_SETUPS, - [] - ); - } - - public updateIdfSetupKeys(setupKeys: string[]) { - this.ctx.globalState.update(ESP.GlobalConfiguration.IDF_SETUPS, setupKeys); - } } diff --git a/src/component-manager/utils.ts b/src/component-manager/utils.ts index b40a2e088..6499f5060 100644 --- a/src/component-manager/utils.ts +++ b/src/component-manager/utils.ts @@ -16,12 +16,14 @@ import { existsSync } from "fs"; import { Logger } from "../logger/logger"; -import { spawn, appendIdfAndToolsToPath } from "../utils"; +import { spawn } from "../utils"; import { CancellationToken, Uri, l10n } from "vscode"; import { readParameter } from "../idfConfiguration"; import { join } from "path"; import { getVirtualEnvPythonPath } from "../pythonManager"; import { EOL } from "os"; +import { configureEnvVariables } from "../common/prepareEnv"; +import { ESP } from "../config"; export async function addDependency( workspace: Uri, @@ -30,10 +32,13 @@ export async function addDependency( cancelToken: CancellationToken ) { try { - const idfPathDir = readParameter("idf.espIdfPath", workspace); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPathDir = currentEnvVars["IDF_PATH"]; const idfPy = join(idfPathDir, "tools", "idf.py"); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); - const pythonBinPath = await getVirtualEnvPythonPath(workspace); + const modifiedEnv = await configureEnvVariables(workspace); + const pythonBinPath = await getVirtualEnvPythonPath(); const enableCCache = readParameter( "idf.enableCCache", workspace @@ -78,10 +83,13 @@ export async function createProject( example: string ): Promise { try { - const idfPathDir = readParameter("idf.espIdfPath"); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPathDir = currentEnvVars["IDF_PATH"]; const idfPy = join(idfPathDir, "tools", "idf.py"); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); - const pythonBinPath = await getVirtualEnvPythonPath(workspace); + const modifiedEnv = await configureEnvVariables(workspace); + const pythonBinPath = await getVirtualEnvPythonPath(); if ( !existsSync(idfPathDir) || diff --git a/src/config.ts b/src/config.ts index eec066350..6ebd78399 100644 --- a/src/config.ts +++ b/src/config.ts @@ -30,6 +30,7 @@ export namespace ESP { export namespace ProjectConfiguration { export let store: ProjectConfigStore; export const SELECTED_CONFIG = "SELECTED_PROJECT_CONFIG"; + export const CURRENT_IDF_CONFIGURATION = "CURRENT_IDF_CONFIGURATION"; export const PROJECT_CONFIGURATION_FILENAME = "esp_idf_project_configuration.json"; } @@ -53,21 +54,15 @@ export namespace ESP { export namespace GlobalConfiguration { export let store: ExtensionConfigStore; - export const IDF_SETUPS = "IDF_SETUPS"; export const SELECTED_WORKSPACE_FOLDER = "SELECTED_WORKSPACE_FOLDER"; } export const platformDepConfigurations: string[] = [ - "idf.buildPath", "idf.espIdfPath", - "idf.espAdfPath", - "idf.espMdfPath", - "idf.espRainmakerPath", - "idf.espHomeKitSdkPath", + "idf.toolsPath", + "idf.buildPath", "idf.gitPath", - "idf.pythonBinPath", "idf.port", - "idf.toolsPath", ]; export namespace Rainmaker { @@ -140,6 +135,10 @@ export namespace ESP { export const README = ESP.URL.GithubRepository + "/blob/master/README.md"; export const FLASH_ENCRYPTION = "/security/flash-encryption.html"; } + + export namespace InstallManager { + export const Releases = "https://dl.espressif.com/dl/eim/index.html"; + } } export namespace Webview { diff --git a/src/coverage/configureProject.ts b/src/coverage/configureProject.ts index f7247f34e..5abe01e41 100644 --- a/src/coverage/configureProject.ts +++ b/src/coverage/configureProject.ts @@ -16,7 +16,11 @@ * limitations under the License. */ -import { extensionContext, getConfigValueFromSDKConfig, getEspIdfFromCMake } from "../utils"; +import { + extensionContext, + getConfigValueFromSDKConfig, + getEspIdfFromCMake, +} from "../utils"; import { NotificationMode, readParameter } from "../idfConfiguration"; import { ConfserverProcess } from "../espIdf/menuconfig/confServerProcess"; import { @@ -27,8 +31,12 @@ import { Progress, CancellationToken, } from "vscode"; -import { getDocsLocaleLang, getDocsVersion } from "../espIdf/documentation/getDocsVersion"; +import { + getDocsLocaleLang, + getDocsVersion, +} from "../espIdf/documentation/getDocsVersion"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { ESP } from "../config"; export async function configureProjectWithGcov(workspacePath: Uri) { const appTraceDestTrax = await getConfigValueFromSDKConfig( @@ -70,7 +78,9 @@ export async function configureProjectWithGcov(workspacePath: Uri) { appTraceGcovEnable === "y"; if (isGcovEnabled) { - return window.showInformationMessage("Code coverage is already enabled in sdkconfig"); + return window.showInformationMessage( + "Code coverage is already enabled in sdkconfig" + ); } if (!ConfserverProcess.exists()) { @@ -120,13 +130,13 @@ export async function configureProjectWithGcov(workspacePath: Uri) { export async function openCoverageUrl(workspacePath: Uri) { const docsVersions = await getDocsVersion(); - const idfPath = - readParameter("idf.espIdfPath", workspacePath) || process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; let idfVersion = "v" + (await getEspIdfFromCMake(idfPath)); let idfTarget = await getIdfTargetFromSdkconfig(workspacePath); - let docVersion = docsVersions.find( - (docVer) => docVer.name === idfVersion - ); + let docVersion = docsVersions.find((docVer) => docVer.name === idfVersion); let targetToUse: string = "esp32"; if (!docVersion) { docVersion = docsVersions.find((docVer) => docVer.name === "latest"); diff --git a/src/coverage/gcdaPaths.ts b/src/coverage/gcdaPaths.ts index ace4eb81a..21c45791b 100644 --- a/src/coverage/gcdaPaths.ts +++ b/src/coverage/gcdaPaths.ts @@ -20,12 +20,11 @@ import { readdir, stat } from "fs-extra"; import { extname, join } from "path"; import { Uri } from "vscode"; import { getGcovExecutable } from "./coverageService"; -import { readParameter } from "../idfConfiguration"; import { exec } from "child_process"; -import { appendIdfAndToolsToPath } from "../utils"; import { IGcovOutput } from "./gcovData"; import { Logger } from "../logger/logger"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { configureEnvVariables } from "../common/prepareEnv"; export async function getGcdaPaths(workspaceFolder: Uri) { const gcdaPaths: Set = new Set(); @@ -64,7 +63,7 @@ export async function getGcovData(workspaceFolder: Uri) { } return new Promise(async (resolve, reject) => { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const modifiedEnv = await configureEnvVariables(workspaceFolder); exec( command, { diff --git a/src/customTasks/customTaskProvider.ts b/src/customTasks/customTaskProvider.ts index 6456ca0c0..5eb0bf366 100644 --- a/src/customTasks/customTaskProvider.ts +++ b/src/customTasks/customTaskProvider.ts @@ -28,8 +28,8 @@ import { } from "vscode"; import { NotificationMode, readParameter } from "../idfConfiguration"; import { TaskManager } from "../taskManager"; -import { appendIdfAndToolsToPath } from "../utils"; import { ShellOutputCapturingExecution } from "../taskManager/customExecution"; +import { configureEnvVariables } from "../common/prepareEnv"; export enum CustomTaskType { Custom = "custom", @@ -84,7 +84,7 @@ export class CustomTask { if (!command) { return; } - const modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); + const modifiedEnv = await configureEnvVariables(this.currentWorkspace); const options: ShellExecutionOptions = { cwd: this.currentWorkspace.fsPath, env: modifiedEnv, diff --git a/src/downloadManager.ts b/src/downloadManager.ts deleted file mode 100644 index b1c3b61bb..000000000 --- a/src/downloadManager.ts +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as del from "del"; -import * as fs from "fs"; -import { ensureDir, pathExists } from "fs-extra"; -import * as path from "path"; -import * as vscode from "vscode"; -import axios, { AxiosRequestConfig, AxiosResponse, CancelToken } from "axios"; -import { IdfToolsManager } from "./idfToolsManager"; -import { IFileInfo, IPackage } from "./IPackage"; -import { Logger } from "./logger/logger"; -import { OutputChannel } from "./logger/outputChannel"; -import { PackageError } from "./packageError"; -import { PackageProgress } from "./PackageProgress"; -import { PackageManagerWebError } from "./packageWebError"; -import * as utils from "./utils"; -import { ESP } from "./config"; - -export class DownloadManager { - private readonly MAX_RETRIES = 5; - private readonly TIMEOUT = 30000; // 30 seconds - - constructor( - private installPath: string, - private refreshUIRate: number = 0.5 - ) {} - - public getToolPackagesPath(toolPackage: string[]) { - return path.resolve(this.installPath, ...toolPackage); - } - - public downloadPackages( - idfToolsManager: IdfToolsManager, - progress: vscode.Progress<{ message?: string; increment?: number }>, - mirror: ESP.IdfMirror, - pkgsProgress?: PackageProgress[], - cancelToken?: vscode.CancellationToken, - onReqPkgs?: string[] - ): Promise { - return idfToolsManager.getPackageList(onReqPkgs).then((packages) => { - let count: number = 1; - return utils.buildPromiseChain( - packages, - (pkg): Promise => { - let pkgProgressToUse: PackageProgress; - if (pkgsProgress) { - pkgProgressToUse = pkgsProgress.find((pkgProgress) => { - return pkgProgress.name === pkg.name; - }); - } - const p: Promise = this.downloadPackage( - idfToolsManager, - mirror, - pkg, - `${count}/${packages.length}`, - progress, - pkgProgressToUse, - cancelToken - ); - count += 1; - return p; - } - ); - }); - } - - public async downloadPackage( - idfToolsManager: IdfToolsManager, - mirror: ESP.IdfMirror, - pkg: IPackage, - progressCount: string, - progress: vscode.Progress<{ message?: string; increment?: number }>, - pkgProgress?: PackageProgress, - cancelToken?: vscode.CancellationToken - ): Promise { - progress.report({ message: `Downloading ${progressCount}: ${pkg.name}` }); - this.appendChannel(`Downloading ${pkg.description}`); - const urlInfoToUse = idfToolsManager.obtainUrlInfoForPlatform(pkg); - if (mirror == ESP.IdfMirror.Espressif) { - urlInfoToUse.url = idfToolsManager.applyGithubAssetsMapping( - urlInfoToUse.url - ); - } - this.appendChannel( - `Using mirror ${ - mirror == ESP.IdfMirror.Espressif ? "Espressif" : "Github" - } with URL ${urlInfoToUse.url}` - ); - await this.downloadPackageWithRetries( - pkg, - urlInfoToUse, - pkgProgress, - cancelToken - ); - } - - public async downloadPackageWithRetries( - pkg: IPackage, - urlInfoToUse: IFileInfo, - pkgProgress?: PackageProgress, - cancelToken?: vscode.CancellationToken - ): Promise { - const fileName = utils.fileNameFromUrl(urlInfoToUse.url); - const destPath = this.getToolPackagesPath(["dist"]); - const absolutePath: string = this.getToolPackagesPath(["dist", fileName]); - - const pkgExists = await pathExists(absolutePath); - if (pkgExists) { - const checksumEqual = await utils.validateFileSizeAndChecksum( - absolutePath, - urlInfoToUse.sha256, - urlInfoToUse.size - ); - if (checksumEqual) { - pkgProgress.FileMatchChecksum = checksumEqual; - this.appendChannel( - `Found ${pkg.name} in ${this.installPath + path.sep}dist` - ); - pkgProgress.Progress = `100.00%`; - pkgProgress.ProgressDetail = `(${(urlInfoToUse.size / 1024).toFixed( - 2 - )} / ${(urlInfoToUse.size / 1024).toFixed(2)}) KB`; - return; - } else { - this.appendChannel( - `Checksum mismatch for ${pkg.name}, will resume download` - ); - // Don't delete the file - we'll resume from where we left off - } - } - - // Download with resume capability - await this.downloadWithResume( - urlInfoToUse.url, - destPath, - pkgProgress, - cancelToken, - urlInfoToUse.size - ); - - // Validate the downloaded file - pkgProgress.FileMatchChecksum = await utils.validateFileSizeAndChecksum( - absolutePath, - urlInfoToUse.sha256, - urlInfoToUse.size - ); - } - - /** - * Download file with resume capability using HTTP Range requests - */ - public async downloadWithResume( - urlToUse: string, - destPath: string, - pkgProgress?: PackageProgress, - cancelToken?: vscode.CancellationToken, - expectedSize?: number - ): Promise { - const fileName = utils.fileNameFromUrl(urlToUse); - const absolutePath = path.resolve(destPath, fileName); - - // Ensure destination directory exists - await ensureDir(destPath, { mode: 0o775 }); - - let retryCount = 0; - let lastError: Error; - - while (retryCount < this.MAX_RETRIES) { - try { - let startByte = 0; - - if (await pathExists(absolutePath)) { - const stats = await fs.promises.stat(absolutePath); - startByte = stats.size; - - // Check if file is already complete - if (expectedSize && startByte >= expectedSize) { - this.appendChannel( - `File ${fileName} already exists and appears to be complete (${startByte} bytes >= ${expectedSize} expected)` - ); - if (pkgProgress) { - pkgProgress.Progress = "100.00%"; - pkgProgress.ProgressDetail = `(${(expectedSize / 1024).toFixed( - 2 - )} / ${(expectedSize / 1024).toFixed(2)}) KB`; - } - return { status: 200, data: null }; // Return success for completed download - } - - if (startByte > 0) { - this.appendChannel( - `Resuming download of ${fileName} from byte ${startByte}` - ); - } - } - - // Check if server supports range requests - const supportsRange = await this.checkRangeSupport(urlToUse); - - let response: any; - - if (supportsRange && startByte > 0) { - // Resume download using range requests - response = await this.downloadWithRange( - urlToUse, - absolutePath, - startByte, - pkgProgress, - cancelToken, - expectedSize - ); - } else { - // Full download (either no range support or starting from beginning) - if (startByte > 0) { - this.appendChannel( - `Server doesn't support range requests, starting fresh download` - ); - await del(absolutePath, { force: true }); - } - response = await this.downloadFull( - urlToUse, - absolutePath, - pkgProgress, - cancelToken, - expectedSize - ); - } - - this.appendChannel(`Successfully downloaded ${fileName}`); - return response; - } catch (error) { - lastError = error; - retryCount++; - - // Handle 416 Range Not Satisfiable error specifically - if (error.response && error.response.status === 416) { - this.appendChannel( - `Received 416 Range Not Satisfiable for ${fileName}. File may already be complete.` - ); - - // Check if the file exists and has reasonable size - if (await pathExists(absolutePath)) { - const stats = await fs.promises.stat(absolutePath); - if (stats.size > 0) { - this.appendChannel( - `File ${fileName} exists with size ${stats.size} bytes. Treating as complete.` - ); - if (pkgProgress) { - const size = expectedSize || stats.size; - pkgProgress.Progress = "100.00%"; - pkgProgress.ProgressDetail = `(${(size / 1024).toFixed(2)} / ${( - size / 1024 - ).toFixed(2)}) KB`; - } - return { status: 200, data: null }; // Return success for completed download - } - } - } - - if (cancelToken && cancelToken.isCancellationRequested) { - throw new PackageError( - "Download cancelled by user", - "downloadWithResume" - ); - } - - if (retryCount >= this.MAX_RETRIES) { - this.appendChannel( - `Failed to download ${urlToUse} after ${this.MAX_RETRIES} attempts` - ); - throw error; - } - - // Calculate exponential backoff delay - const delay = Math.min(1000 * Math.pow(2, retryCount - 1), 10000); - this.appendChannel( - `Download failed (attempt ${retryCount}/${this.MAX_RETRIES}). Retrying in ${delay}ms...` - ); - this.appendChannel(`Error: ${error.message || error}`); - - // Wait before retry - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } - - throw lastError; - } - - /** - * Check if server supports HTTP Range requests - */ - private async checkRangeSupport(url: string): Promise { - try { - const config: AxiosRequestConfig = { - method: "HEAD", - timeout: this.TIMEOUT, - headers: { - "User-Agent": ESP.HTTP_USER_AGENT, - }, - }; - - const response = await axios.head(url, config); - const acceptRanges = response.headers["accept-ranges"]; - const contentRange = response.headers["content-range"]; - - return acceptRanges === "bytes" || !!contentRange; - } catch (error) { - this.appendChannel(`Could not check range support: ${error.message}`); - return false; - } - } - - /** - * Download file using HTTP Range requests for resume capability - */ - private async downloadWithRange( - url: string, - filePath: string, - startByte: number, - pkgProgress?: PackageProgress, - cancelToken?: vscode.CancellationToken, - expectedSize?: number - ): Promise { - const fileName = path.basename(filePath); - const fileStream = fs.createWriteStream(filePath, { - flags: "a", - mode: 0o775, - }); // Append mode - - const config: AxiosRequestConfig = { - method: "GET", - timeout: this.TIMEOUT, - responseType: "stream", - headers: { - "User-Agent": ESP.HTTP_USER_AGENT, - Range: `bytes=${startByte}-`, - }, - cancelToken: cancelToken - ? this.createCancelToken(cancelToken) - : undefined, - }; - - this.appendChannel( - `Downloading ${fileName} with range request from byte ${startByte}` - ); - - try { - const response = await axios.get(url, config); - - if (response.status !== 206 && response.status !== 200) { - throw new PackageManagerWebError( - null, - "HTTP Range Request Error", - "downloadWithRange", - `Unexpected status code: ${response.status}`, - response.status.toString() - ); - } - const contentRange = response.headers["content-range"]; - let totalSize = expectedSize || 0; - - // Parse content-range header to get total size - if (contentRange) { - const match = contentRange.match(/bytes \d+-\d+\/(\d+)/); - if (match) { - totalSize = parseInt(match[1], 10); - } - } - - let downloadedSize = startByte; - let lastProgressUpdate = 0; - - response.data.on("data", (chunk: Buffer) => { - downloadedSize += chunk.length; - - if (pkgProgress && totalSize > 0) { - const progress = (downloadedSize / totalSize) * 100; - const progressDetail = `(${(downloadedSize / 1024).toFixed(2)} / ${( - totalSize / 1024 - ).toFixed(2)}) KB`; - - // Update progress only if significant change - if ( - progress - lastProgressUpdate >= this.refreshUIRate || - downloadedSize === totalSize - ) { - pkgProgress.Progress = `${progress.toFixed(2)}%`; - pkgProgress.ProgressDetail = progressDetail; - lastProgressUpdate = progress; - } - } - }); - - response.data.pipe(fileStream); - - return new Promise((resolve, reject) => { - fileStream.on("finish", () => { - this.appendChannel(`Range download completed: ${fileName}`); - resolve(response); - }); - - fileStream.on("error", (error) => { - reject( - new PackageError( - `File write error: ${error.message}`, - "downloadWithRange", - error - ) - ); - }); - - response.data.on("error", (error) => { - reject( - new PackageError( - `Download stream error: ${error.message}`, - "downloadWithRange", - error - ) - ); - }); - }); - } catch (error) { - // Close the file stream if it was opened - if (fileStream) { - fileStream.end(); - } - - // Re-throw the error to be handled by the calling method - throw error; - } - } - - /** - * Download full file (no resume capability) - */ - private async downloadFull( - url: string, - filePath: string, - pkgProgress?: PackageProgress, - cancelToken?: vscode.CancellationToken, - expectedSize?: number - ): Promise { - const fileName = path.basename(filePath); - const fileStream = fs.createWriteStream(filePath, { mode: 0o775 }); - - const config: AxiosRequestConfig = { - method: "GET", - timeout: this.TIMEOUT, - responseType: "stream", - headers: { - "User-Agent": ESP.HTTP_USER_AGENT, - }, - cancelToken: cancelToken - ? this.createCancelToken(cancelToken) - : undefined, - }; - - this.appendChannel(`Downloading ${fileName} (full download)`); - - const response = await axios.get(url, config); - - if (response.status !== 200) { - throw new PackageManagerWebError( - null, - "HTTP Download Error", - "downloadFull", - `Unexpected status code: ${response.status}`, - response.status.toString() - ); - } - - const contentLength = parseInt( - response.headers["content-length"] || "0", - 10 - ); - const totalSize = expectedSize || contentLength; - let downloadedSize = 0; - let lastProgressUpdate = 0; - - response.data.on("data", (chunk: Buffer) => { - downloadedSize += chunk.length; - - if (pkgProgress && totalSize > 0) { - const progress = (downloadedSize / totalSize) * 100; - const progressDetail = `(${(downloadedSize / 1024).toFixed(2)} / ${( - totalSize / 1024 - ).toFixed(2)}) KB`; - - // Update progress only if significant change - if ( - progress - lastProgressUpdate >= this.refreshUIRate || - downloadedSize === totalSize - ) { - pkgProgress.Progress = `${progress.toFixed(2)}%`; - pkgProgress.ProgressDetail = progressDetail; - lastProgressUpdate = progress; - } - } - }); - - response.data.pipe(fileStream); - - return new Promise((resolve, reject) => { - fileStream.on("finish", () => { - this.appendChannel( - `Full download completed: ${fileName} into ${filePath}` - ); - resolve(response); - }); - - fileStream.on("error", (error) => { - reject( - new PackageError( - `File write error: ${error.message}`, - "downloadFull", - error - ) - ); - }); - - response.data.on("error", (error) => { - reject( - new PackageError( - `Download stream error: ${error.message}`, - "downloadFull", - error - ) - ); - }); - }); - } - - /** - * Create axios cancel token from VS Code cancellation token - */ - private createCancelToken( - cancelToken: vscode.CancellationToken - ): CancelToken { - const source = axios.CancelToken.source(); - - cancelToken.onCancellationRequested(() => { - source.cancel("Download cancelled by user"); - }); - - return source.token; - } - - private appendChannel(text: string): void { - OutputChannel.appendLine(text); - Logger.info(text); - } -} diff --git a/src/efuse/index.ts b/src/efuse/index.ts index 274893c77..2fbe98303 100644 --- a/src/efuse/index.ts +++ b/src/efuse/index.ts @@ -25,6 +25,7 @@ import { Logger } from "../logger/logger"; import { Uri, l10n } from "vscode"; import { getVirtualEnvPythonPath } from "../pythonManager"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { ESP } from "../config"; export type ESPEFuseSummary = { [category: string]: [ @@ -51,8 +52,10 @@ export class ESPEFuseManager { private idfPath: string; constructor(private workspace: Uri) { - this.idfPath = - readParameter("idf.espIdfPath", workspace) || process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + this.idfPath = currentEnvVars["IDF_PATH"] || process.env.IDF_PATH; } async summary(): Promise { @@ -78,7 +81,7 @@ export class ESPEFuseManager { async readSummary() { const tempFile = join(tmpdir(), "espefusejsondump.tmp"); - const pythonPath = await getVirtualEnvPythonPath(this.workspace); + const pythonPath = await getVirtualEnvPythonPath(); const port = await readSerialPort(this.workspace, false); if (!port) { diff --git a/src/eim/downloadInstall.ts b/src/eim/downloadInstall.ts new file mode 100644 index 000000000..23dafd80c --- /dev/null +++ b/src/eim/downloadInstall.ts @@ -0,0 +1,466 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Monday, 28th April 2025 4:34:49 pm + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import axios from "axios"; +import { tmpdir } from "os"; +import { basename, dirname, extname, join, resolve as pathResolve } from "path"; +import { + createWriteStream, + ensureDir, + move, + pathExists, + ReadStream, + remove, + WriteStream, +} from "fs-extra"; +import { CancellationToken, env, Progress, window } from "vscode"; +import { OutputChannel } from "../logger/outputChannel"; +import del from "del"; +import { dirExistPromise } from "../utils"; +import * as yauzl from "yauzl"; +import { Logger } from "../logger/logger"; +import { getEimIdfJson } from "./getExistingSetups"; +import { readParameter } from "../idfConfiguration"; + +export async function runExistingEIM( + progress: Progress<{ message: string; increment: number }>, + cancelToken: CancellationToken +): Promise { + let eimPath = ""; + if (cancelToken.isCancellationRequested) { + return false; + } + progress.report({ + message: `Checking EIM already exists...`, + increment: 0, + }); + try { + const eimJSON = await getEimIdfJson(); + if (eimJSON && eimJSON.eimPath) { + const doesEimPathExists = await pathExists(eimJSON.eimPath); + if (doesEimPathExists) { + eimPath = eimJSON.eimPath; + } + } + // 2. Check EIM_PATH env variable if not found in eim_idf.json + if (!eimPath) { + eimPath = process.env.EIM_PATH || ""; + } + // 3. Use default path based on platform if still not found + if (!eimPath) { + if (process.platform === "win32") { + eimPath = join( + process.env.USERPROFILE || "", + ".espressif", + "eim_gui", + "eim.exe" + ); + } else if (process.platform === "darwin") { + eimPath = "/Applications/eim.app"; + } else if (process.platform === "linux") { + eimPath = join(process.env.HOME || "", ".espressif", "eim_gui", "eim"); + } + } + const doesEimPathExists = await pathExists(eimPath); + if (!doesEimPathExists) { + throw new Error(`EIM not found at ${eimPath}`); + } + } catch (error) { + Logger.error( + `Error while running existing EIM: ${error.message}`, + error, + "runExistingEIM" + ); + return false; + } + + progress.report({ + message: `EIM found at ${eimPath}. Launching...`, + increment: 0, + }); + + // Read idf.eimExecutableArgs with utils.readParameter and use it to run EIM + const idfEimExecutableArgs = readParameter( + "idf.eimExecutableArgs" + ) as string[]; + const argsString = idfEimExecutableArgs.join(" "); + + let binaryPath = ""; + if (process.platform === "win32") { + binaryPath = `& '${eimPath.replace(/'/g, "''")}'${ + argsString ? " " + argsString : "" + }`; + } else if (process.platform === "linux") { + binaryPath = `./${basename(eimPath)}${argsString ? " " + argsString : ""}`; + } else if (process.platform === "darwin") { + binaryPath = `open ${eimPath}${argsString ? " --args " + argsString : ""}`; + } + const shellPath = + process.platform === "win32" + ? "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + : env.shell; + const espIdfTerminal = window.createTerminal({ + name: "ESP-IDF EIM", + shellPath: shellPath, + cwd: dirname(eimPath), + }); + espIdfTerminal.sendText(binaryPath, true); + espIdfTerminal.sendText("exit"); + return true; +} + +export async function downloadExtractAndRunEIM( + progress: Progress<{ message: string; increment: number }>, + cancelToken: CancellationToken, + useMirror: boolean = false +): Promise { + const jsonUrl = "https://dl.espressif.com/dl/eim/eim_unified_release.json"; + // Determine EIM install path + let eimInstallPath = ""; + if (process.platform === "win32") { + eimInstallPath = join( + process.env.USERPROFILE || "", + ".espressif", + "eim_gui" + ); + } else if (process.platform === "darwin") { + eimInstallPath = "/Applications"; + } else if (process.platform === "linux") { + eimInstallPath = join(process.env.HOME || "", ".espressif", "eim_gui"); + } + + try { + progress.report({ + message: `Downloading EIM versions...`, + increment: 0, + }); + const response = await axios.get(jsonUrl, { + headers: { "Cache-Control": "no-cache" }, + }); + const data = response.data; + + const arch = process.arch; + let osKey: string; + + if (process.platform === "darwin") { + osKey = + arch === "arm64" + ? "eim-gui-macos-aarch64.zip" + : "eim-gui-macos-x64.zip"; + } else if (process.platform === "win32") { + osKey = "eim-gui-windows-x64.exe"; + } else if (process.platform === "linux") { + osKey = + arch === "arm64" + ? "eim-gui-linux-aarch64.zip" + : "eim-gui-linux-x64.zip"; + } else { + throw new Error(`Unsupported platform: ${process.platform}`); + } + const fileInfo = data.assets.find((asset: any) => asset.name === osKey); + if (!fileInfo) { + throw new Error(`No file found for OS and architecture: ${osKey}`); + } + + progress.report({ + message: `Downloading EIM: ${fileInfo.browser_download_url}`, + increment: 0, + }); + OutputChannel.appendLine( + `Downloading EIM: ${fileInfo.browser_download_url}`, + "EIM Download" + ); + + let downloadUrl = fileInfo.browser_download_url; + const fileName = basename(downloadUrl); + const downloadPath = join(eimInstallPath, fileName); + + const doesDownloadPathExists = await pathExists(downloadPath); + if (!doesDownloadPathExists) { + if (useMirror) { + downloadUrl = downloadUrl.replace( + "https://github.com", + "https://dl.espressif.com/github_assets" + ); + } + + const doesTmpDirExists = await pathExists(eimInstallPath); + if (!doesTmpDirExists) { + await ensureDir(eimInstallPath); + } + + const writeStream: WriteStream = createWriteStream(downloadPath, { + mode: 0o755, + }); + const fileResponseStream = await axios({ + method: "get", + url: downloadUrl, + responseType: "stream", + }); + const { headers } = await axios.head(downloadUrl); + const totalSize = parseInt(headers["content-length"], 10); + + let downloadedSize = 0; + fileResponseStream.data.on("data", (chunk: Buffer) => { + if (cancelToken.isCancellationRequested) { + fileResponseStream.data.destroy(); + throw new Error("Download canceled by user."); + } + downloadedSize += chunk.length; + if (totalSize) { + const percentCompleted = Math.round( + (downloadedSize * 100) / totalSize + ); + const increment = Math.round((chunk.length * 100) / totalSize); + progress.report({ + message: `Downloading EIM... ${percentCompleted}%`, + increment, + }); + } + }); + fileResponseStream.data.pipe(writeStream); + + await new Promise((resolve, reject) => { + fileResponseStream.data.on("end", resolve); + fileResponseStream.data.on("error", reject); + }); + OutputChannel.appendLine( + `File downloaded and extracted to: ${downloadPath}` + ); + } else { + OutputChannel.appendLine(`Using existing: ${downloadPath}`); + progress.report({ + message: `Using existing: ${downloadPath}`, + increment: 0, + }); + } + + if (process.platform !== "win32") { + await installZipFile(downloadPath, eimInstallPath, cancelToken); + Logger.infoNotify(`File ${osKey} extracted to: ${eimInstallPath}`); + } + + let binaryPath = ""; + if (process.platform === "win32") { + binaryPath = `Start-Process -FilePath "${join( + eimInstallPath, + "eim.exe" + )}"`; + } else if (process.platform === "linux") { + binaryPath = `./eim`; + } else if (process.platform === "darwin") { + binaryPath = `open ${join(eimInstallPath, "eim.app")}`; + } + const shellPath = + process.platform === "win32" + ? "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + : env.shell; + const espIdfTerminal = window.createTerminal({ + name: "ESP-IDF EIM", + shellPath: shellPath, + cwd: eimInstallPath, + }); + espIdfTerminal.sendText(binaryPath, true); + espIdfTerminal.sendText("exit"); + } catch (error) { + Logger.errorNotify( + `Error during download and extraction: ${error.message}`, + error, + "downloadAndExtractEIM" + ); + } +} + +export class ZipFileError extends Error { + constructor( + public message: string, + public methodName: string, + public innerError: any = null, + public errorCode: string = " " + ) { + super(message); + this.errorCode = errorCode; + this.innerError = innerError; + this.methodName = methodName; + } +} + +export async function installZipFile( + zipFilePath: string, + destPath: string, + cancelToken?: CancellationToken +) { + return new Promise(async (resolve, reject) => { + const doesZipFileExists = await pathExists(zipFilePath); + if (!doesZipFileExists) { + return reject(`File ${zipFilePath} doesn't exist.`); + } + yauzl.open(zipFilePath, { lazyEntries: true }, (error, zipfile) => { + if (error) { + return reject( + new ZipFileError("Zip file error", "InstallZipFile", error) + ); + } + if (cancelToken && cancelToken.isCancellationRequested) { + return reject( + new ZipFileError("Install cancelled by user", "InstallZipFile") + ); + } + + zipfile.on("end", () => { + return resolve(); + }); + zipfile.on("error", (err) => { + return reject( + new ZipFileError("Zip File error", "InstallZipFile", err) + ); + }); + + zipfile.readEntry(); + zipfile.on("entry", async (entry: yauzl.Entry) => { + const absolutePath: string = pathResolve(destPath, entry.fileName); + const dirExists = await dirExistPromise(absolutePath); + if (dirExists) { + try { + await del(absolutePath, { force: true }); + } catch (err) { + OutputChannel.appendLine( + `Error deleting previous ${absolutePath}: ${err.message}` + ); + return reject( + new ZipFileError( + "Install folder cant be deleted", + "InstallZipFile", + err, + err.errorCode + ) + ); + } + } + if (entry.fileName.endsWith("/")) { + try { + await ensureDir(absolutePath, { mode: 0o775 }); + zipfile.readEntry(); + } catch (err) { + return reject( + new ZipFileError( + "Error creating directory", + "InstallZipFile", + err + ) + ); + } + } else { + const exists = await pathExists(absolutePath); + if (!exists) { + zipfile.openReadStream( + entry, + async (err, readStream: ReadStream) => { + if (err) { + return reject( + new ZipFileError( + "Error reading zip stream", + "InstallZipFile", + err + ) + ); + } + readStream.on("error", (openErr) => { + return reject( + new ZipFileError( + "Error in readstream", + "InstallZipFile", + openErr + ) + ); + }); + + try { + await ensureDir(dirname(absolutePath), { + mode: 0o775, + }); + } catch (mkdirErr) { + return reject( + new ZipFileError( + "Error creating directory", + "InstallZipFile", + mkdirErr + ) + ); + } + const absoluteEntryTmpPath: string = absolutePath + ".tmp"; + const doesTmpPathExists = await pathExists( + absoluteEntryTmpPath + ); + if (doesTmpPathExists) { + try { + await remove(absoluteEntryTmpPath); + } catch (rmError) { + return reject( + new ZipFileError( + `Error unlinking tmp file ${absoluteEntryTmpPath}`, + "InstallZipFile", + rmError + ) + ); + } + } + const writeStream: WriteStream = createWriteStream( + absoluteEntryTmpPath, + { mode: 0o755 } + ); + writeStream.on("error", (writeStreamErr) => { + return reject( + new ZipFileError( + "Error in writeStream", + "InstallZipFile", + writeStreamErr + ) + ); + }); + writeStream.on("close", async () => { + try { + await move(absoluteEntryTmpPath, absolutePath); + } catch (closeWriteStreamErr) { + return reject( + new ZipFileError( + `Error renaming file ${absoluteEntryTmpPath}`, + "InstallZipFile", + closeWriteStreamErr + ) + ); + } + zipfile.readEntry(); + }); + readStream.pipe(writeStream); + } + ); + } else { + if (extname(absolutePath) !== ".txt") { + OutputChannel.appendLine( + `Warning File ${absolutePath} + already exists and was not updated.` + ); + } + zipfile.readEntry(); + } + } + }); + }); + }); +} diff --git a/src/eim/getExistingSetups.ts b/src/eim/getExistingSetups.ts new file mode 100644 index 000000000..e23d675e3 --- /dev/null +++ b/src/eim/getExistingSetups.ts @@ -0,0 +1,133 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Friday, 29th November 2024 3:28:00 pm + * Copyright 2024 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { pathExists, readJson } from "fs-extra"; +import { join } from "path"; +import { readParameter } from "../idfConfiguration"; +import { Logger } from "../logger/logger"; +import { EspIdfJson, IdfSetup } from "./types"; +import { getEspIdfFromCMake } from "../utils"; +import { loadIdfSetupsFromEspIdfJson } from "./migrationTool"; +import { ESP } from "../config"; +import { Uri } from "vscode"; + +export async function getIdfSetups(workspaceFolder: Uri) { + const workspaceFolderUri = ESP.GlobalConfiguration.store.get( + ESP.GlobalConfiguration.SELECTED_WORKSPACE_FOLDER + ); + const customVars = readParameter( + "idf.customExtraVars", + workspaceFolderUri + ) as { + [key: string]: string; + }; + const eimIDFSetups = await loadIdfSetupsFromEimIdfJson(); + let resultingIdfSetups = eimIDFSetups; + if (customVars["IDF_TOOLS_PATH"]) { + const espIdfCustomVarsJsonSetups = await loadIdfSetupsFromEspIdfJson( + customVars["IDF_TOOLS_PATH"] + ); + resultingIdfSetups = resultingIdfSetups.concat(espIdfCustomVarsJsonSetups); + } + if (process.env.IDF_TOOLS_PATH) { + const espIdfSysJsonSetups = await loadIdfSetupsFromEspIdfJson( + process.env["IDF_TOOLS_PATH"] + ); + resultingIdfSetups = resultingIdfSetups.concat(espIdfSysJsonSetups); + } + const containerPath = + process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; + const defaultIdfToolsPath = join(containerPath, ".espressif"); + const espIdfSysJsonSetups = await loadIdfSetupsFromEspIdfJson( + defaultIdfToolsPath + ); + resultingIdfSetups = resultingIdfSetups.concat(espIdfSysJsonSetups); + + + const oldIdfToolsPath = readParameter("idf.toolsPath", workspaceFolder) as string; + if (oldIdfToolsPath) { + const oldIdfSetups = await loadIdfSetupsFromEspIdfJson(oldIdfToolsPath); + resultingIdfSetups = resultingIdfSetups.concat(oldIdfSetups); + } + + resultingIdfSetups = resultingIdfSetups.filter( + (setup, index, self) => + index === + self.findIndex( + (s) => s.idfPath === setup.idfPath && s.toolsPath === setup.toolsPath + ) + ); + + return resultingIdfSetups; +} + +export async function loadIdfSetupsFromEimIdfJson() { + let idfSetups: IdfSetup[] = []; + const espIdfJson = await getEimIdfJson(); + if ( + espIdfJson && + espIdfJson.idfInstalled && + Object.keys(espIdfJson.idfInstalled).length + ) { + for (let idfInstalled of espIdfJson.idfInstalled) { + const idfVersion = await getEspIdfFromCMake(idfInstalled.path); + let setupConf: IdfSetup = { + activationScript: idfInstalled.activationScript, + id: idfInstalled.id, + idfPath: idfInstalled.path, + isValid: false, + gitPath: espIdfJson.gitPath, + version: idfVersion, + toolsPath: idfInstalled.idfToolsPath, + python: idfInstalled.python, + sysPythonPath: "", + } as IdfSetup; + idfSetups.push(setupConf); + } + } + return idfSetups; +} + +export async function getEimIdfJson() { + const espIdeJsonCustomPath = readParameter("idf.eimIdfJsonPath"); + const espIdePathExists = await pathExists(espIdeJsonCustomPath); + let eimIdfJsonPath = ""; + if (espIdePathExists) { + eimIdfJsonPath = espIdeJsonCustomPath; + } else { + eimIdfJsonPath = + process.platform === "win32" + ? join("C:", "Espressif", "tools", "eim_idf.json") + : join(process.env.HOME, ".espressif", "tools", "eim_idf.json"); + } + const espIdfJsonExists = await pathExists(eimIdfJsonPath); + let espIdfJson: EspIdfJson; + try { + if (!espIdfJsonExists) { + throw new Error(`${eimIdfJsonPath} doesn't exists.`); + } + espIdfJson = await readJson(eimIdfJsonPath); + return espIdfJson; + } catch (error) { + const msg = + error && error.message + ? error.message + : `Error reading ${eimIdfJsonPath}.`; + Logger.error(msg, error, "getEimIdfJson"); + } +} diff --git a/src/eim/loadIdfSetup.ts b/src/eim/loadIdfSetup.ts new file mode 100644 index 000000000..3969dae80 --- /dev/null +++ b/src/eim/loadIdfSetup.ts @@ -0,0 +1,179 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Monday, 24th February 2025 5:31:26 pm + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { commands, ConfigurationTarget, l10n, Uri, window } from "vscode"; +import { ESP } from "../config"; +import { getIdfSetups } from "./getExistingSetups"; +import { IdfSetup } from "./types"; +import { getEnvVariables } from "./loadSettings"; +import { readParameter, writeParameter } from "../idfConfiguration"; +import { getEspIdfFromCMake } from "../utils"; +import { join } from "path"; +import { isIdfSetupValid } from "./verifySetup"; +import { Logger } from "../logger/logger"; +import { createHash } from "crypto"; + +export async function loadIdfSetup(workspaceFolder: Uri) { + const idfSetups = await getIdfSetups(workspaceFolder); + + if (!idfSetups || idfSetups.length < 1) { + window.showInformationMessage(l10n.t("No ESP-IDF Setups found")); + Logger.info("Using loadEnvVarsAsIdfSetup to configure extension"); + const idfEnvSetup = await loadEnvVarsAsIdfSetup(workspaceFolder); + if (idfEnvSetup) { + return idfEnvSetup; + } + } + const currentIdfConfigurationName = readParameter( + "idf.currentSetup", + workspaceFolder + ) as string; + + let idfSetupToUse: IdfSetup; + if (idfSetups.length > 0) { + if (currentIdfConfigurationName) { + idfSetupToUse = idfSetups.find((idfSetup) => { + return idfSetup.idfPath === currentIdfConfigurationName; + }); + } else { + const oldIdfPath = readParameter( + "idf.espIdfPath", + workspaceFolder + ) as string; + if (oldIdfPath) { + idfSetupToUse = idfSetups.find((idfSetup) => { + return idfSetup.idfPath === oldIdfPath; + }); + } + if (!idfSetupToUse) { + idfSetupToUse = idfSetups[0]; + } + await writeParameter( + "idf.currentSetup", + idfSetupToUse.idfPath, + ConfigurationTarget.WorkspaceFolder, + workspaceFolder + ); + } + } + + if (!idfSetupToUse) { + Logger.infoNotify("Current ESP-IDF setup is not found."); + + const openESPIDFManager = l10n.t( + "Open ESP-IDF Installation Manager" + ) as string; + const action = await window.showInformationMessage( + l10n.t("The extension configuration is not valid. Choose an action:"), + openESPIDFManager + ); + if (!action) { + return; + } + + if (action === openESPIDFManager) { + commands.executeCommand("espIdf.installManager"); + return; + } + } + + await writeParameter( + "idf.gitPath", + idfSetupToUse.gitPath, + ConfigurationTarget.Global + ); + + const envVars = await getEnvVariables(idfSetupToUse); + + ESP.ProjectConfiguration.store.set( + ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, + envVars + ); + return idfSetupToUse; +} + +function getIdfMd5sum(idfPath: string) { + if (!idfPath) { + return ""; + } + const md5Value = createHash("md5") + .update(idfPath.replace(/\\/g, "/")) + .digest("hex"); + return `esp-idf-${md5Value}`; +} + +export async function loadEnvVarsAsIdfSetup(workspaceFolder: Uri) { + const customVars = readParameter("idf.customExtraVars", workspaceFolder) as { + [key: string]: string; + }; + + const idfPath = process.env.IDF_PATH || customVars["IDF_PATH"]; + const containerPath = + process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; + const defaultIdfToolsPath = join(containerPath, ".espressif"); + const idfToolsPath = + process.env.IDF_TOOLS_PATH || + customVars["IDF_TOOLS_PATH"] || + defaultIdfToolsPath; + const gitPath = "/usr/bin/git"; + const idfSetupId = getIdfMd5sum(idfPath); + const idfVersion = await getEspIdfFromCMake(idfPath); + const pyDir = + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python3"]; + let venvPythonPath = ""; + if (process.env.IDF_PYTHON_ENV_PATH || customVars["IDF_PYTHON_ENV_PATH"]) { + venvPythonPath = join( + process.env.IDF_PYTHON_ENV_PATH || customVars["IDF_PYTHON_ENV_PATH"], + ...pyDir + ); + } + const envDefinedIdfSetup: IdfSetup = { + id: idfSetupId, + activationScript: "", + idfPath, + gitPath, + toolsPath: idfToolsPath, + sysPythonPath: "", + version: idfVersion, + python: venvPythonPath, + isValid: false, + }; + let reason = ""; + [envDefinedIdfSetup.isValid, reason] = await isIdfSetupValid( + envDefinedIdfSetup + ); + + if (!envDefinedIdfSetup.isValid) { + Logger.infoNotify(l10n.t("ESP-IDF Setup is not valid: {0}", reason), { + category: "espIdf.installManager", + reason, + }); + return; + } + + if (envDefinedIdfSetup.isValid) { + const envVars = await getEnvVariables(envDefinedIdfSetup); + ESP.ProjectConfiguration.store.set( + ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, + envVars + ); + return envDefinedIdfSetup; + } +} diff --git a/src/eim/loadSettings.ts b/src/eim/loadSettings.ts new file mode 100644 index 000000000..6f3d53886 --- /dev/null +++ b/src/eim/loadSettings.ts @@ -0,0 +1,100 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Thursday, 6th February 2025 11:42:36 am + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EOL } from "os"; +import { spawn } from "../utils"; +import { IdfSetup } from "./types"; +import { delimiter, join } from "path"; +import { getEnvVariablesFromIdfSetup } from "./migrationTool"; +import { Logger } from "../logger/logger"; +import { env } from "vscode"; + +export async function getEnvVariables(idfSetup: IdfSetup) { + if (idfSetup.activationScript) { + return await getEnvVariablesFromActivationScript(idfSetup.activationScript); + } else { + return await getEnvVariablesFromIdfSetup(idfSetup); + } +} + +export async function getEnvVariablesFromActivationScript( + activationScriptPath: string +) { + try { + const args = + process.platform === "win32" + ? [ + "-ExecutionPolicy", + "Bypass", + "-NoProfile", + `'${activationScriptPath.replace(/'/g, "''")}'`, + "-e", + ] + : [`"${activationScriptPath}"`, "-e"]; + const shellPath = + process.platform === "win32" + ? "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + : env.shell; + const envVarsOutput = await spawn(shellPath, args, { + maxBuffer: 500 * 1024, + cwd: process.cwd(), + shell: shellPath, + }); + const envVarsArray = envVarsOutput.toString().trim().split(/\r?\n/g); + let envDict: { [key: string]: string } = {}; + for (const envVar of envVarsArray) { + let keyIndex = envVar.indexOf("="); + if (keyIndex === -1) { + continue; + } + let varKey = envVar.slice(0, keyIndex).trim(); + let varValue = envVar.slice(keyIndex + 1).trim(); + envDict[varKey] = varValue; + } + + let pathNameInEnv: string = Object.keys(process.env).find( + (k) => k.toUpperCase() == "PATH" + ); + + if (envDict[pathNameInEnv]) { + envDict[pathNameInEnv] = envDict[pathNameInEnv] + .replace(process.env[pathNameInEnv], "") + .replace(new RegExp(`(^${delimiter}|${delimiter}$)`, "g"), ""); + } + + const pyDir = + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python"]; + envDict["PYTHON"] = join(envDict["IDF_PYTHON_ENV_PATH"], ...pyDir); + + return envDict; + } catch (error) { + const errMsg = + error && error.message + ? error.message + : "Error getting Env variables from EIM activation script"; + Logger.error( + errMsg, + error, + "loadSettings getEnvVariablesFromActivationScript", + undefined, + false + ); + } +} diff --git a/src/eim/migrationTool.ts b/src/eim/migrationTool.ts new file mode 100644 index 000000000..103dbc21b --- /dev/null +++ b/src/eim/migrationTool.ts @@ -0,0 +1,211 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Monday, 16th December 2024 5:48:20 pm + * Copyright 2024 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { dirname, join } from "path"; +import { execChildProcess, getEspIdfFromCMake } from "../utils"; +import { IdfSetup } from "./types"; +import { pathExists, readJson } from "fs-extra"; +import { ESP } from "../config"; +import { getEnvVarsFromIdfTools, getUnixPythonList } from "../pythonManager"; +import { IdfToolsManager } from "../idfToolsManager"; + +export async function getSystemPython( + espIdfPath: string, + espIdfToolsPath: string +) { + if (process.platform !== "win32") { + const sysPythonList = await getUnixPythonList(__dirname); + return sysPythonList && sysPythonList.length ? sysPythonList[0] : "python3"; + } else { + const idfVersion = await getEspIdfFromCMake(espIdfPath); + const pythonVersionToUse = + idfVersion >= "5.0" + ? ESP.URL.IDF_EMBED_PYTHON.VERSION + : ESP.URL.OLD_IDF_EMBED_PYTHON.VERSION; + const idfPythonPath = join( + espIdfToolsPath, + "tools", + "idf-python", + pythonVersionToUse, + "python.exe" + ); + const idfPythonPathExists = await pathExists(idfPythonPath); + return idfPythonPathExists ? idfPythonPath : ""; + } +} + +export async function getIdfPythonEnvPath( + espIdfDir: string, + idfToolsDir: string, + pythonBin: string +) { + const pythonCode = `import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))`; + const args = ["-c", pythonCode]; + const rawPythonVersion = await execChildProcess(pythonBin, args, espIdfDir); + if (!rawPythonVersion) { + throw new Error("Failed to retrieve Python version. The result is empty."); + } + const pythonVersion = rawPythonVersion.replace(/(\n|\r|\r\n)/gm, ""); + const fullEspIdfVersion = await getEspIdfFromCMake(espIdfDir); + const majorMinorMatches = fullEspIdfVersion.match(/([0-9]+\.[0-9]+).*/); + const espIdfVersion = + majorMinorMatches && majorMinorMatches.length > 0 + ? majorMinorMatches[1] + : "x.x"; + const resultVersion = `idf${espIdfVersion}_py${pythonVersion}_env`; + return join(idfToolsDir, "python_env", resultVersion); +} + +export async function getPythonEnvPath( + espIdfDir: string, + idfToolsDir: string, + pythonBin: string +) { + const idfPyEnvPath = await getIdfPythonEnvPath( + espIdfDir, + idfToolsDir, + pythonBin + ); + const pyDir = + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python"]; + const fullIdfPyEnvPath = join(idfPyEnvPath, ...pyDir); + const pyEnvPathExists = await pathExists(fullIdfPyEnvPath); + return pyEnvPathExists ? fullIdfPyEnvPath : ""; +} + +export async function getEnvVariablesFromIdfSetup(idfSetup: IdfSetup) { + let envVars: { [key: string]: string } = {}; + envVars["IDF_PATH"] = idfSetup.idfPath; + envVars["IDF_TOOLS_PATH"] = idfSetup.toolsPath; + envVars["ESP_IDF_VERSION"] = idfSetup.version; + const idfToolsManager = await IdfToolsManager.createIdfToolsManager( + idfSetup.idfPath + ); + const exportedToolsPaths = await idfToolsManager.exportPathsInString( + join(idfSetup.toolsPath, "tools"), + ["cmake", "ninja"] + ); + envVars["PATH"] = exportedToolsPaths; + const idfToolsVars = await idfToolsManager.exportVars(idfSetup.toolsPath); + + for (const toolVar in idfToolsVars) { + envVars[toolVar] = idfToolsVars[toolVar]; + } + + if (!idfSetup.python) { + if (!idfSetup.sysPythonPath) { + idfSetup.sysPythonPath = await getSystemPython( + idfSetup.idfPath, + idfSetup.toolsPath + ); + } + + idfSetup.python = await getPythonEnvPath( + idfSetup.idfPath, + idfSetup.toolsPath, + idfSetup.sysPythonPath + ); + } + const pythonExists = await pathExists(idfSetup.python); + + if (pythonExists) { + envVars["PYTHON"] = idfSetup.python; + envVars["IDF_PYTHON_ENV_PATH"] = dirname(dirname(idfSetup.python)); + const idfVars = await getEnvVarsFromIdfTools( + idfSetup.idfPath, + idfSetup.toolsPath, + idfSetup.python + ); + for (const idfVar in idfVars) { + envVars[idfVar] = idfVars[idfVar]; + } + } + return envVars; +} + +export async function loadIdfSetupsFromEspIdfJson(toolsPath: string) { + const espIdfJson = await loadEspIdfJson(toolsPath); + let idfSetups: IdfSetup[] = []; + if ( + espIdfJson && + espIdfJson.idfInstalled && + Object.keys(espIdfJson.idfInstalled).length + ) { + for (let idfInstalledKey of Object.keys(espIdfJson.idfInstalled)) { + let setupConf: IdfSetup = { + id: idfInstalledKey, + idfPath: espIdfJson.idfInstalled[idfInstalledKey].path, + gitPath: espIdfJson.gitPath, + version: espIdfJson.idfInstalled[idfInstalledKey].version, + python: espIdfJson.idfInstalled[idfInstalledKey].python, + toolsPath: toolsPath, + isValid: false, + } as IdfSetup; + idfSetups.push(setupConf); + } + } + return idfSetups; +} + +export interface EspIdfJson { + $schema: string; + $id: string; + _comment: string; + _warning: string; + gitPath: string; + idfToolsPath: string; + idfSelectedId: string; + idfInstalled: { [key: string]: IdfInstalled }; +} + +export interface IdfInstalled { + version: string; + python: string; + path: string; +} + +export function getEspIdfJsonTemplate(toolsPath: string) { + return { + $schema: "http://json-schema.org/schema#", + $id: "http://dl.espressif.com/dl/schemas/esp_idf", + _comment: "Configuration file for ESP-IDF IDEs.", + _warning: + "Use / or \\ when specifying path. Single backslash is not allowed by JSON format.", + gitPath: "", + idfToolsPath: toolsPath, + idfSelectedId: "", + idfInstalled: {}, + } as EspIdfJson; +} + +export async function loadEspIdfJson(toolsPath: string) { + const espIdfJsonPath = join(toolsPath, "esp_idf.json"); + const espIdfJsonExists = await pathExists(espIdfJsonPath); + let espIdfJson: EspIdfJson; + try { + if (!espIdfJsonExists) { + throw new Error(`${espIdfJsonPath} doesn't exists.`); + } + espIdfJson = await readJson(espIdfJsonPath); + } catch (error) { + espIdfJson = getEspIdfJsonTemplate(toolsPath); + } + return espIdfJson; +} diff --git a/src/views/setup/types.ts b/src/eim/types.ts similarity index 51% rename from src/views/setup/types.ts rename to src/eim/types.ts index a43d90868..9043f789b 100644 --- a/src/views/setup/types.ts +++ b/src/eim/types.ts @@ -1,27 +1,20 @@ /* * Project: ESP-IDF VSCode Extension - * File Created: Thursday, 31st August 2023 8:11:39 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * + * File Created: Wednesday, 11th December 2024 2:32:14 pm + * Copyright 2024 Espressif Systems (Shanghai) CO LTD + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -export interface IEspIdfLink { - filename: string; - name: string; - mirror: string; - url: string; - version: string; -} export interface IdfSetup { id: string; @@ -29,43 +22,29 @@ export interface IdfSetup { toolsPath: string; idfPath: string; gitPath: string; - python?: string; - sysPythonPath?: string; isValid: boolean; + activationScript: string; + python: string; + sysPythonPath: string; } -export enum IdfMirror { - Espressif, - Github, +export interface EspIdfJson { + $schema: string; + $id: string; + _comment: string; + _warning: string; + gitPath: string; + idfToolsPath: string; + idfSelectedId: string; + idfInstalled: IdfInstalled[]; + eimPath: string; } -export interface IDownload { +export interface IdfInstalled { + activationScript: string; id: string; - progress: string; - progressDetail: string; -} - -export interface IEspIdfTool extends IDownload { - actual: string; - description: string; - doesToolExist: boolean; - env: {}; - expected: string; - hashResult: boolean; - hasFailed: boolean; + idfToolsPath: string; name: string; path: string; -} - -export enum StatusType { - failed, - installed, - pending, - started, -} - -export enum SetupMode { - advanced, - express, - existing, -} + python: string; +} \ No newline at end of file diff --git a/src/eim/verifySetup.ts b/src/eim/verifySetup.ts new file mode 100644 index 000000000..188fb6585 --- /dev/null +++ b/src/eim/verifySetup.ts @@ -0,0 +1,191 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Wednesday, 11th December 2024 3:05:43 pm + * Copyright 2024 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { pathExists } from "fs-extra"; +import { Logger } from "../logger/logger"; +import { IdfSetup } from "./types"; +import { startPythonReqsProcess } from "../utils"; +import { IdfToolsManager, IEspIdfTool } from "../idfToolsManager"; +import { join } from "path"; +import { ConfigurationTarget, StatusBarItem, Uri } from "vscode"; +import { writeParameter } from "../idfConfiguration"; +import { CommandKeys, createCommandDictionary } from "../cmdTreeView/cmdStore"; +import { getEnvVariables } from "./loadSettings"; +import { ESP } from "../config"; + +/** + * Validate that given IDF Setup is valid. + * @param {IdfSetup} idfSetup IDF Setup to validate. + * @param logToChannel If output IDF Tools validation result in output channel + * @returns {[boolean, string]} Tuple: True if IDF Setup is valid, False otherwise, and fail reason + */ +export async function isIdfSetupValid( + idfSetup: IdfSetup, + logToChannel = true +): Promise<[boolean, string]> { + try { + let envVars: { [key: string]: string } = await getEnvVariables(idfSetup); + let venvPythonPath: string = ""; + if (idfSetup.python) { + venvPythonPath = idfSetup.python; + } else { + const pyDir = + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python3"]; + venvPythonPath = join(envVars["IDF_PYTHON_ENV_PATH"], ...pyDir); + } + + if (!envVars["IDF_PATH"]) { + return [false, "IDF_PATH is not set in environment variables"]; + } + const doesIdfPathExists = await pathExists(envVars["IDF_PATH"]); + if (!doesIdfPathExists) { + return [false, `IDF_PATH does not exist: ${envVars["IDF_PATH"]}`]; + } + + const pathNameInEnv: string = Object.keys(envVars).find( + (k) => k.toUpperCase() == "PATH" + ); + + const idfToolsManager = await IdfToolsManager.createIdfToolsManager( + envVars["IDF_PATH"] + ); + let toolsInfo: IEspIdfTool[] = []; + const activationScriptPathExists = await pathExists( + idfSetup.activationScript + ); + if (!activationScriptPathExists) { + const exportedToolsPaths = await idfToolsManager.exportPathsInString( + join(idfSetup.toolsPath, "tools"), + ["cmake", "ninja"] + ); + toolsInfo = await idfToolsManager.getRequiredToolsInfo( + join(idfSetup.toolsPath, "tools"), + exportedToolsPaths, + ["cmake", "ninja"], + logToChannel + ); + } else { + toolsInfo = await idfToolsManager.getEIMToolsInfo( + envVars[pathNameInEnv], + ["cmake", "ninja"], + logToChannel + ); + } + + const failedToolsResult = toolsInfo.filter( + (tInfo) => + !tInfo.doesToolExist && ["cmake", "ninja"].indexOf(tInfo.name) === -1 + ); + + if (failedToolsResult.length) { + const missingTools = failedToolsResult.map((t) => t.name).join(", "); + return [false, `Missing required tools: ${missingTools}`]; + } + const [pyEnvReqsValid, pyEnvReqsMsg] = await checkPyVenv( + venvPythonPath, + envVars["IDF_PATH"] + ); + if (!pyEnvReqsValid) { + return [ + pyEnvReqsValid, + pyEnvReqsMsg || + "Python virtual environment or requirements are not satisfied", + ]; + } + return [true, ""]; + } catch (error) { + const msg = + error && error.message + ? error.message + : `Error checking EIM Idf Setup for script ${idfSetup.activationScript}`; + Logger.error(msg, error, "verifySetup isIdfSetupValid"); + return [false, msg]; + } +} + +export async function checkPyVenv( + pyVenvPath: string, + espIdfPath: string +): Promise<[boolean, string]> { + const pyExists = await pathExists(pyVenvPath); + if (!pyExists) { + return [false, `${pyVenvPath} does not exist.`]; + } + let requirements: string; + requirements = join( + espIdfPath, + "tools", + "requirements", + "requirements.core.txt" + ); + const coreRequirementsExists = await pathExists(requirements); + if (!coreRequirementsExists) { + requirements = join(espIdfPath, "requirements.txt"); + const requirementsExists = await pathExists(requirements); + if (!requirementsExists) { + return [false, `${requirements} doesn't exist.`]; + } + } + const reqsResults = await startPythonReqsProcess( + pyVenvPath, + espIdfPath, + requirements + ); + if (reqsResults.indexOf("are not satisfied") > -1) { + return [false, reqsResults]; + } + return [true, ""]; +} + +export async function saveSettings( + setupConf: IdfSetup, + workspaceFolderUri: Uri, + espIdfStatusBar: StatusBarItem +) { + await writeParameter( + "idf.currentSetup", + setupConf.idfPath, + ConfigurationTarget.WorkspaceFolder, + workspaceFolderUri + ); + + const envVars = await getEnvVariables(setupConf); + + if (setupConf.python) { + envVars["PYTHON"] = setupConf.python; + } + + ESP.ProjectConfiguration.store.set( + ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, + envVars + ); + await writeParameter( + "idf.gitPath", + setupConf.gitPath, + ConfigurationTarget.Global + ); + if (espIdfStatusBar) { + const commandDictionary = createCommandDictionary(); + espIdfStatusBar.text = `$(${ + commandDictionary[CommandKeys.SelectCurrentIdfVersion].iconId + }) ESP-IDF v${setupConf.version}`; + } + Logger.infoNotify("ESP-IDF has been configured"); +} diff --git a/src/espAdf/espAdfDownload.ts b/src/espAdf/espAdfDownload.ts index babd1cef5..5a49e7904 100644 --- a/src/espAdf/espAdfDownload.ts +++ b/src/espAdf/espAdfDownload.ts @@ -30,5 +30,5 @@ export class AdfCloning extends AbstractCloning { export async function getEspAdf(workspace?: Uri) { const gitPath = (await readParameter("idf.gitPath", workspace)) || "git"; const adfInstaller = new AdfCloning(gitPath); - await adfInstaller.getRepository("idf.espAdfPath", workspace); + await adfInstaller.getRepository("ADF_PATH", workspace); } diff --git a/src/espBom/index.ts b/src/espBom/index.ts index 485351655..b9ed2c19b 100644 --- a/src/espBom/index.ts +++ b/src/espBom/index.ts @@ -27,7 +27,6 @@ import { ProcessExecution, } from "vscode"; import { - appendIdfAndToolsToPath, canAccessFile, execChildProcess, } from "../utils"; @@ -38,6 +37,7 @@ import { pathExists, lstat, constants } from "fs-extra"; import { Logger } from "../logger/logger"; import { TaskManager } from "../taskManager"; import { getVirtualEnvPythonPath } from "../pythonManager"; +import { configureEnvVariables } from "../common/prepareEnv"; export async function createSBOM(workspaceUri: Uri) { try { @@ -52,7 +52,7 @@ export async function createSBOM(workspaceUri: Uri) { `${projectDescriptionJson} doesn't exists for ESP-IDF SBOM tasks.` ); } - const modifiedEnv = await appendIdfAndToolsToPath(workspaceUri); + const modifiedEnv = await configureEnvVariables(workspaceUri); const sbomFilePath = readParameter( "idf.sbomFilePath", workspaceUri @@ -142,8 +142,8 @@ export async function createSBOM(workspaceUri: Uri) { } export async function installEspSBOM(workspace: Uri) { - const pythonBinPath = await getVirtualEnvPythonPath(workspace); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); + const pythonBinPath = await getVirtualEnvPythonPath(); + const modifiedEnv = await configureEnvVariables(workspace); try { const showResult = await execChildProcess( pythonBinPath, diff --git a/src/espHomekit/espHomekitDownload.ts b/src/espHomekit/espHomekitDownload.ts index 295bf1f63..f142d070f 100644 --- a/src/espHomekit/espHomekitDownload.ts +++ b/src/espHomekit/espHomekitDownload.ts @@ -35,5 +35,5 @@ export class EspHomekitCloning extends AbstractCloning { export async function getEspHomeKitSdk(workspace: Uri) { const gitPath = (await readParameter("idf.gitPath", workspace)) || "git"; const homeKitInstaller = new EspHomekitCloning(gitPath); - await homeKitInstaller.getRepository("idf.espHomeKitSdkPath", workspace); + await homeKitInstaller.getRepository("HOMEKIT_PATH", workspace); } diff --git a/src/espIdf/core-dump/esp-core-dump-py-tool.ts b/src/espIdf/core-dump/esp-core-dump-py-tool.ts index a84523eec..327125550 100644 --- a/src/espIdf/core-dump/esp-core-dump-py-tool.ts +++ b/src/espIdf/core-dump/esp-core-dump-py-tool.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Friday, 3rd July 2020 5:49:06 pm * Copyright 2020 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,9 +17,10 @@ */ import { join } from "path"; -import { appendIdfAndToolsToPath, spawn } from "../../utils"; +import { spawn } from "../../utils"; import { Logger } from "../../logger/logger"; import { Uri } from "vscode"; +import { configureEnvVariables } from "../../common/prepareEnv"; export enum InfoCoreFileFormat { Base64 = "b64", @@ -49,7 +50,7 @@ export class ESPCoreDumpPyTool { public async generateCoreELFFile(options: CoreELFGenerationOptions) { let resp: Buffer; try { - const env = await appendIdfAndToolsToPath(options.workspaceUri); + const env = await configureEnvVariables(options.workspaceUri); resp = await spawn( options.pythonBinPath, [ diff --git a/src/espIdf/debugAdapter/checkPyReqs.ts b/src/espIdf/debugAdapter/checkPyReqs.ts deleted file mode 100644 index 99ca49c36..000000000 --- a/src/espIdf/debugAdapter/checkPyReqs.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Friday, 23rd February 2024 6:13:58 pm - * Copyright 2024 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { join } from "path"; -import { readParameter } from "../../idfConfiguration"; -import { pathExists } from "fs-extra"; -import { Uri } from "vscode"; -import { extensionContext, startPythonReqsProcess } from "../../utils"; -import { getVirtualEnvPythonPath } from "../../pythonManager"; - -export async function checkDebugAdapterRequirements(workspaceFolder: Uri) { - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); - let requirementsPath = join( - extensionContext.extensionPath, - "esp_debug_adapter", - "requirements.txt" - ); - let checkResult: string; - try { - const doesPyTestRequirementsExists = await pathExists(requirementsPath); - if (!doesPyTestRequirementsExists) { - return false; - } - checkResult = await startPythonReqsProcess( - pythonBinPath, - idfPath, - requirementsPath - ); - } catch (error) { - checkResult = error && error.message ? error.message : " are not satisfied"; - } - if (checkResult.indexOf("are satisfied") > -1) { - return true; - } - return false; -} diff --git a/src/espIdf/debugAdapter/debugAdapterManager.ts b/src/espIdf/debugAdapter/debugAdapterManager.ts deleted file mode 100644 index 6fd5e52eb..000000000 --- a/src/espIdf/debugAdapter/debugAdapterManager.ts +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Friday, 12th July 2019 5:59:07 pm - * Copyright 2019 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ChildProcess, spawn } from "child_process"; -import { EventEmitter } from "events"; -import * as path from "path"; -import * as vscode from "vscode"; -import * as idfConf from "../../idfConfiguration"; -import { Logger } from "../../logger/logger"; -import { - appendIdfAndToolsToPath, - canAccessFile, - getToolchainToolName, - isBinInPath, - PreCheck, -} from "../../utils"; -import { EOL } from "os"; -import { outputFile, constants } from "fs-extra"; -import { createFlashModel } from "../../flash/flashModelBuilder"; -import { OutputChannel } from "../../logger/outputChannel"; -import { getVirtualEnvPythonPath } from "../../pythonManager"; -import { getIdfTargetFromSdkconfig } from "../../workspaceConfig"; - -export interface IDebugAdapterConfig { - appOffset?: string; - tmoScaleFactor?: number; - coreDumpFile?: string; - currentWorkspace?: vscode.Uri; - debugAdapterPort?: number; - elfFile?: string; - env?: NodeJS.ProcessEnv; - gdbinitFilePath?: string; - initGdbCommands?: string[]; - isPostMortemDebugMode: boolean; - isOocdDisabled: boolean; - logLevel?: number; - target?: string; -} - -export class DebugAdapterManager extends EventEmitter { - public static init(context: vscode.ExtensionContext): DebugAdapterManager { - if (!DebugAdapterManager.instance) { - DebugAdapterManager.instance = new DebugAdapterManager(context); - } - return DebugAdapterManager.instance; - } - private static instance: DebugAdapterManager; - - private adapter: ChildProcess; - private appOffset: string; - private chan: Buffer; - private coreDumpFile: string; - private currentWorkspace: vscode.Uri; - private debugAdapterPath: string; - private elfFile: string; - private env; - private gdbinitFilePath: string; - private initGdbCommands: string[]; - private tmoScaleFactor?: number; - private isPostMortemDebugMode: boolean; - private isOocdDisabled: boolean; - private logLevel: number; - private port: number; - private target: string; - - private constructor(context: vscode.ExtensionContext) { - super(); - this.configureWithDefaultValues(context.extensionUri); - OutputChannel.init(); - this.chan = Buffer.alloc(0); - } - - public start() { - return new Promise(async (resolve, reject) => { - if (this.isRunning()) { - return; - } - if (!isBinInPath("openocd", this.env)) { - return reject( - new Error("Invalid OpenOCD bin path or access is denied for the user") - ); - } - if ( - this.env && - !this.isOocdDisabled && - typeof this.env.OPENOCD_SCRIPTS === "undefined" - ) { - return reject( - new Error( - "Invalid OpenOCD script path or access is denied for the user" - ) - ); - } - if (!canAccessFile(this.elfFile, constants.R_OK)) { - return reject(new Error(`${this.elfFile} doesn't exist. Build first.`)); - } - const logFile = path.join(this.currentWorkspace.fsPath, "debug") + ".log"; - - if (!this.appOffset) { - const serialPort = await idfConf.readSerialPort(this.currentWorkspace, false); - if (!serialPort) { - return reject( - new Error( - vscode.l10n.t( - "No serial port found for current IDF_TARGET: {0}", - this.target - ) - ) - ); - } - const flashBaudRate = await idfConf.readParameter( - "idf.flashBaudRate", - this.currentWorkspace - ); - const buildDirPath = idfConf.readParameter( - "idf.buildPath", - this.currentWorkspace - ) as string; - const flasherArgsJsonPath = path.join( - buildDirPath, - "flasher_args.json" - ); - if (!canAccessFile(flasherArgsJsonPath, constants.R_OK)) { - return reject( - new Error(`${flasherArgsJsonPath} doesn't exist. Build first.`) - ); - } - const model = await createFlashModel( - flasherArgsJsonPath, - serialPort, - flashBaudRate - ); - this.appOffset = model.app.address; - } - const pythonBinPath = await getVirtualEnvPythonPath( - this.currentWorkspace - ); - - const toolchainPrefix = getToolchainToolName(this.target, ""); - const adapterArgs = [ - this.debugAdapterPath, - "-d", - this.logLevel.toString(), - "-e", - this.elfFile, - "-l", - logFile, - "-p", - this.port.toString(), - "-dn", - this.target, - "-a", - this.appOffset, - "-t", - toolchainPrefix, - ]; - if (this.isPostMortemDebugMode) { - adapterArgs.push("-pm"); - } - if (this.coreDumpFile) { - adapterArgs.push("-c", this.coreDumpFile); - } - if (this.isOocdDisabled) { - adapterArgs.push("-om", "without_oocd"); - } - const resultGdbInitFile = this.gdbinitFilePath - ? this.gdbinitFilePath - : await this.makeGdbinitFile(); - if (resultGdbInitFile) { - adapterArgs.push("-x", resultGdbInitFile); - } - if (this.tmoScaleFactor) { - adapterArgs.push("-tsf", this.tmoScaleFactor.toString()); - } - this.adapter = spawn(pythonBinPath, adapterArgs, { env: this.env }); - - this.adapter.stderr.on("data", (data) => { - data = typeof data === "string" ? Buffer.from(data) : data; - this.sendToOutputChannel(data); - OutputChannel.appendLine(data.toString(), "Debug Adapter"); - Logger.info(data.toString()); - this.emit("error", data, this.chan); - }); - - this.adapter.stdout.on("data", (data) => { - data = typeof data === "string" ? Buffer.from(data) : data; - this.sendToOutputChannel(data); - OutputChannel.appendLine(data.toString(), "Debug Adapter"); - Logger.info(data.toString()); - this.emit("data", this.chan); - if (data.toString().trim().endsWith("DEBUG_ADAPTER_READY2CONNECT")) { - return resolve(true); - } - }); - - this.adapter.on("error", (error) => { - this.emit("error", error, this.chan); - this.stop(); - return reject(error); - }); - - this.adapter.on("close", (code: number, signal: string) => { - if (!signal && code && code !== 0) { - Logger.errorNotify( - `ESP-IDF Debug Adapter exit with error code ${code}`, - new Error("Spawn exit with non-zero" + code), - "DebugAdapterManager start" - ); - } - this.stop(); - }); - OutputChannel.show(); - }); - } - - public stop() { - if (this.adapter && !this.adapter.killed) { - this.isPostMortemDebugMode = false; - this.initGdbCommands = []; - this.adapter.kill("SIGKILL"); - this.adapter = undefined; - const stoppedMsg = "[Stopped] : ESP-IDF Debug Adapter"; - Logger.info(stoppedMsg); - OutputChannel.appendLine(stoppedMsg); - } - } - - public configureAdapter(config: IDebugAdapterConfig) { - if (config.coreDumpFile) { - this.coreDumpFile = config.coreDumpFile; - } - if (config.currentWorkspace) { - this.currentWorkspace = config.currentWorkspace; - } - if (config.debugAdapterPort) { - this.port = config.debugAdapterPort; - } - if (config.elfFile) { - this.elfFile = config.elfFile; - } - if (config.env) { - for (const envVar of Object.keys(config.env)) { - this.env[envVar] = config.env[envVar]; - } - } - if (config.gdbinitFilePath) { - this.gdbinitFilePath = config.gdbinitFilePath; - } - if (config.initGdbCommands) { - this.initGdbCommands = config.initGdbCommands; - } - this.isPostMortemDebugMode = config.isPostMortemDebugMode; - if (config.logLevel) { - this.logLevel = config.logLevel; - } - if (config.target) { - this.target = config.target; - } - if (config.isOocdDisabled) { - this.isOocdDisabled = config.isOocdDisabled; - } - this.tmoScaleFactor = config.tmoScaleFactor; - this.appOffset = config.appOffset; - } - - public isRunning(): boolean { - return this.adapter && !this.adapter.killed; - } - - private async configureWithDefaultValues(extensionPath: vscode.Uri) { - this.currentWorkspace = PreCheck.isWorkspaceFolderOpen() - ? vscode.workspace.workspaceFolders[0].uri - : extensionPath; - this.debugAdapterPath = path.join( - extensionPath.fsPath, - "esp_debug_adapter", - "debug_adapter_main.py" - ); - this.isPostMortemDebugMode = false; - this.isOocdDisabled = false; - this.port = 43474; - this.logLevel = 0; - let idfTarget = await getIdfTargetFromSdkconfig(this.currentWorkspace); - this.target = idfTarget; - this.env = await appendIdfAndToolsToPath(this.currentWorkspace); - this.env.PYTHONPATH = path.join( - extensionPath.fsPath, - "esp_debug_adapter", - "debug_adapter" - ); - this.initGdbCommands = []; - this.elfFile = ""; - if (this.currentWorkspace) { - const buildDirPath = idfConf.readParameter( - "idf.buildPath", - this.currentWorkspace - ) as string; - this.elfFile = `${path.join(buildDirPath, "project-name")}.elf`; - } - } - - private sendToOutputChannel(data: Buffer) { - this.chan = Buffer.concat([this.chan, data]); - } - - private async makeGdbinitFile() { - try { - if (this.initGdbCommands && this.initGdbCommands.length > 0) { - let result = ""; - for (const initCmd of this.initGdbCommands) { - result = result + initCmd + EOL; - } - const lastValue = result.lastIndexOf(EOL); - result = result.substring(0, lastValue); - - const resultGdbInitPath = path.join( - this.currentWorkspace.fsPath, - "esp-idf-vscode-generated.gdb" - ); - await outputFile(resultGdbInitPath, result); - return resultGdbInitPath; - } - } catch (error) { - Logger.errorNotify( - "Error creating gdbinit file", - error, - "DebugAdapterManager makeGdbinitFile" - ); - } - return; - } -} diff --git a/src/espIdf/debugAdapter/verifyApp.ts b/src/espIdf/debugAdapter/verifyApp.ts index d2fc9357e..1b0a3e768 100644 --- a/src/espIdf/debugAdapter/verifyApp.ts +++ b/src/espIdf/debugAdapter/verifyApp.ts @@ -21,12 +21,13 @@ import { l10n, Uri } from "vscode"; import { createFlashModel } from "../../flash/flashModelBuilder"; import { readParameter, readSerialPort } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; -import { appendIdfAndToolsToPath, spawn } from "../../utils"; +import { spawn } from "../../utils"; import { pathExists } from "fs-extra"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; export async function verifyAppBinary(workspaceFolder: Uri) { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const modifiedEnv = await configureEnvVariables(workspaceFolder); const serialPort = await readSerialPort(workspaceFolder, false); if (!serialPort) { return Logger.warnNotify( @@ -37,10 +38,9 @@ export async function verifyAppBinary(workspaceFolder: Uri) { ); } const flashBaudRate = readParameter("idf.flashBaudRate", workspaceFolder); - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); const esptoolPath = join( - idfPath, + modifiedEnv["IDF_PATH"], "components", "esptool_py", "esptool", diff --git a/src/espIdf/documentation/getDocsVersion.ts b/src/espIdf/documentation/getDocsVersion.ts index e172be774..4917694c9 100644 --- a/src/espIdf/documentation/getDocsVersion.ts +++ b/src/espIdf/documentation/getDocsVersion.ts @@ -13,16 +13,22 @@ // limitations under the License. import { ESP } from "../../config"; -import { pathExists, readFile, readJSON, stat, writeJSON } from "fs-extra"; +import { + createWriteStream, + pathExists, + readFile, + readJSON, + stat, + writeJSON, +} from "fs-extra"; import { tmpdir } from "os"; import { basename, join } from "path"; -import { DownloadManager } from "../../downloadManager"; import jsonic from "jsonic"; import { Logger } from "../../logger/logger"; import { extensionContext, getEspIdfFromCMake } from "../../utils"; import * as vscode from "vscode"; -import * as idfConf from "../../idfConfiguration"; import { getIdfTargetFromSdkconfig } from "../../workspaceConfig"; +import axios from "axios"; export interface IEspIdfDocVersion { name: string; @@ -110,9 +116,8 @@ export async function getDocsIndex( } export async function readObjectFromUrlFile(objectUrl: string) { - const downloadManager = new DownloadManager(tmpdir()); - await downloadManager.downloadWithResume(objectUrl, tmpdir()); const fileName = join(tmpdir(), basename(objectUrl)); + await downloadFile(objectUrl, fileName); const objectStr = await readFile(fileName, "utf-8"); const objectMatches = objectStr.match(/{[\s\S]+}/g); if (objectMatches && objectMatches.length > 0) { @@ -120,6 +125,29 @@ export async function readObjectFromUrlFile(objectUrl: string) { } } +async function downloadFile(url: string, outputLocationPath: string) { + const writer = createWriteStream(outputLocationPath); + try { + const response = await axios({ + url, + method: "GET", + responseType: "stream", + }); + response.data.pipe(writer); + return new Promise((resolve, reject) => { + writer.on("finish", resolve); + writer.on("error", reject); + }); + } catch (error) { + Logger.error( + `Error downloading ${basename(url)}: ${error}`, + error, + "getDocsVersion downloadFile" + ); + throw error; + } +} + /** * Retrieves the URL for the specified documentation part based on the ESP-IDF version and workspace. * @param documentationPart - The documentation part to retrieve the URL for. @@ -130,10 +158,10 @@ export async function getDocsUrl( documentationPart: string, workspace: vscode.Uri ) { - const espIdfPath = idfConf.readParameter( - "idf.espIdfPath", - workspace - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const espIdfPath = currentEnvVars["IDF_PATH"]; const adapterTargetName = await getIdfTargetFromSdkconfig(workspace); const idfVersion = await getEspIdfFromCMake(espIdfPath); diff --git a/src/espIdf/documentation/getSearchResults.ts b/src/espIdf/documentation/getSearchResults.ts index 3fb5651e1..4c99df688 100644 --- a/src/espIdf/documentation/getSearchResults.ts +++ b/src/espIdf/documentation/getSearchResults.ts @@ -17,6 +17,7 @@ import * as idfConf from "../../idfConfiguration"; import { getEspIdfFromCMake } from "../../utils"; import { getDocsBaseUrl, getDocsIndex, getDocsVersion } from "./getDocsVersion"; import { getIdfTargetFromSdkconfig } from "../../workspaceConfig"; +import { ESP } from "../../config"; export class IDocResult { public name: string; @@ -44,9 +45,10 @@ export async function seachInEspDocs( workspaceFolder: Uri ) { const docsVersions = await getDocsVersion(); - const idfPath = - idfConf.readParameter("idf.espIdfPath", workspaceFolder) || - process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; let idfVersion = "v" + (await getEspIdfFromCMake(idfPath)); let idfTarget = await getIdfTargetFromSdkconfig(workspaceFolder); let docVersion = docsVersions.find((docVer) => docVer.name === idfVersion); diff --git a/src/espIdf/menuconfig/confServerProcess.ts b/src/espIdf/menuconfig/confServerProcess.ts index 1d076be68..27793a9a4 100644 --- a/src/espIdf/menuconfig/confServerProcess.ts +++ b/src/espIdf/menuconfig/confServerProcess.ts @@ -24,7 +24,6 @@ import * as idfConf from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { OutputChannel } from "../../logger/outputChannel"; import { - appendIdfAndToolsToPath, delConfigFile, getSDKConfigFilePath, isStringNotEmpty, @@ -33,6 +32,8 @@ import { KconfigMenuLoader } from "./kconfigMenuLoader"; import { Menu, menuType } from "./Menu"; import { MenuConfigPanel } from "./MenuconfigPanel"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; +import { ESP } from "../../config"; export class ConfserverProcess { public static async initWithProgress( @@ -71,8 +72,8 @@ export class ConfserverProcess { public static async init(workspaceFolder: vscode.Uri, extensionPath: string) { return new Promise(async (resolve) => { - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); + const modifiedEnv = await configureEnvVariables(workspaceFolder); if (!ConfserverProcess.instance) { const configFile = await getSDKConfigFilePath(workspaceFolder); ConfserverProcess.instance = new ConfserverProcess( @@ -207,12 +208,13 @@ export class ConfserverProcess { ConfserverProcess.instance.areValuesSaved = true; const currWorkspace = ConfserverProcess.instance.workspaceFolder; await delConfigFile(currWorkspace); - const guiconfigEspPath = - idfConf.readParameter("idf.espIdfPath", currWorkspace) || - process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const guiconfigEspPath = currentEnvVars["IDF_PATH"]; const idfPyPath = path.join(guiconfigEspPath, "tools", "idf.py"); - const modifiedEnv = await appendIdfAndToolsToPath(currWorkspace); - const pythonBinPath = await getVirtualEnvPythonPath(currWorkspace); + const modifiedEnv = await configureEnvVariables(currWorkspace); + const pythonBinPath = await getVirtualEnvPythonPath(); const enableCCache = idfConf.readParameter( "idf.enableCCache", currWorkspace @@ -329,9 +331,10 @@ export class ConfserverProcess { this.workspaceFolder = workspaceFolder; this.extensionPath = extensionPath; this.emitter = new EventEmitter(); - this.espIdfPath = - idfConf.readParameter("idf.espIdfPath", workspaceFolder).toString() || - process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + this.espIdfPath = currentEnvVars["IDF_PATH"]; modifiedEnv.PYTHONUNBUFFERED = "0"; this.configFile = configFile; const idfPath = path.join(this.espIdfPath, "tools", "idf.py"); diff --git a/src/espIdf/menuconfig/saveDefConfig.ts b/src/espIdf/menuconfig/saveDefConfig.ts index ea16f7b18..2607204a5 100644 --- a/src/espIdf/menuconfig/saveDefConfig.ts +++ b/src/espIdf/menuconfig/saveDefConfig.ts @@ -31,9 +31,10 @@ import { TaskManager } from "../../taskManager"; import { NotificationMode, readParameter } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { join } from "path"; -import { appendIdfAndToolsToPath } from "../../utils"; import { pathExists } from "fs-extra"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; +import { ESP } from "../../config"; export async function saveDefSdkconfig( workspaceFolder: Uri, @@ -45,7 +46,10 @@ export async function saveDefSdkconfig( TaskManager.disposeListeners(); }); } - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; const notificationMode = readParameter( "idf.notificationMode", workspaceFolder @@ -92,12 +96,12 @@ export async function getSaveDefConfigExecution( wsFolder: Uri ) { const saveDefConfArgs = [join(idfPath, "tools", "idf.py"), "save-defconfig"]; - const modifiedEnv = await appendIdfAndToolsToPath(wsFolder); + const modifiedEnv = await configureEnvVariables(wsFolder); const options: ProcessExecutionOptions = { cwd: wsFolder.fsPath, env: modifiedEnv, }; - const pythonBinPath = await getVirtualEnvPythonPath(wsFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); const pythonBinExists = await pathExists(pythonBinPath); if (!pythonBinExists) { throw new Error( diff --git a/src/espIdf/monitor/checkWebsocketClient.ts b/src/espIdf/monitor/checkWebsocketClient.ts index 1293af551..116ad7e6a 100644 --- a/src/espIdf/monitor/checkWebsocketClient.ts +++ b/src/espIdf/monitor/checkWebsocketClient.ts @@ -18,12 +18,13 @@ import { Uri } from "vscode"; import { OutputChannel } from "../../logger/outputChannel"; -import { appendIdfAndToolsToPath, execChildProcess } from "../../utils"; +import { execChildProcess } from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; export async function installWebsocketClient(workspace: Uri) { - const pythonBinPath = await getVirtualEnvPythonPath(workspace); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); + const pythonBinPath = await getVirtualEnvPythonPath(); + const modifiedEnv = await configureEnvVariables(workspace); try { const showResult = await execChildProcess( pythonBinPath, diff --git a/src/espIdf/monitor/command.ts b/src/espIdf/monitor/command.ts index b975ed6c0..580c6eaf7 100644 --- a/src/espIdf/monitor/command.ts +++ b/src/espIdf/monitor/command.ts @@ -71,7 +71,7 @@ export async function createNewIdfMonitor( "createNewIdfMonitor select a serial port" ); } - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); if (!utils.canAccessFile(pythonBinPath, R_OK)) { Logger.errorNotify( "Python binary path is not defined", @@ -79,7 +79,10 @@ export async function createNewIdfMonitor( "createNewIdfMonitor pythonBinPath not defined" ); } - const idfPath = readParameter("idf.espIdfPath", workspaceFolder) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; const idfVersion = await utils.getEspIdfFromCMake(idfPath); let sdkMonitorBaudRate: string = await utils.getMonitorBaudRate( workspaceFolder @@ -138,8 +141,6 @@ export async function createNewIdfMonitor( } IDFMonitor.start(); if (noReset) { - const idfPath = readParameter("idf.espIdfPath", workspaceFolder) as string; - const idfVersion = await utils.getEspIdfFromCMake(idfPath); if (idfVersion <= "5.0") { const monitorDelay = readParameter( "idf.monitorDelay", diff --git a/src/espIdf/monitor/index.ts b/src/espIdf/monitor/index.ts index 9d9bdbae2..93480a6b5 100644 --- a/src/espIdf/monitor/index.ts +++ b/src/espIdf/monitor/index.ts @@ -16,8 +16,9 @@ * limitations under the License. */ +import { configureEnvVariables } from "../../common/prepareEnv"; import { ESP } from "../../config"; -import { appendIdfAndToolsToPath, getUserShell } from "../../utils"; +import { getUserShell } from "../../utils"; import { window, Terminal, Uri, env, debug } from "vscode"; import { join } from "path"; @@ -48,9 +49,7 @@ export class IDFMonitor { } static async start() { - const modifiedEnv = await appendIdfAndToolsToPath( - this.config.workspaceFolder - ); + const modifiedEnv = await configureEnvVariables(this.config.workspaceFolder); if (!IDFMonitor.terminal) { IDFMonitor.terminal = window.createTerminal({ name: `ESP-IDF Monitor ${this.config.wsPort ? "(--ws enabled)" : ""}`, diff --git a/src/espIdf/nvs/partitionTable/panel.ts b/src/espIdf/nvs/partitionTable/panel.ts index 0e648a352..4a099fad0 100644 --- a/src/espIdf/nvs/partitionTable/panel.ts +++ b/src/espIdf/nvs/partitionTable/panel.ts @@ -23,6 +23,7 @@ import * as idfConf from "../../../idfConfiguration"; import { canAccessFile, execChildProcess } from "../../../utils"; import { OutputChannel } from "../../../logger/outputChannel"; import { getVirtualEnvPythonPath } from "../../../pythonManager"; +import { ESP } from "../../../config"; export class NVSPartitionTable { private static currentPanel: NVSPartitionTable; @@ -142,11 +143,12 @@ export class NVSPartitionTable { partitionSize: string ) { try { - const idfPathDir = - idfConf.readParameter("idf.espIdfPath", this.workspaceFolder) || - process.env.IDF_PATH; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPathDir = currentEnvVars["IDF_PATH"]; - const pythonBinPath = await getVirtualEnvPythonPath(this.workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); const dirPath = dirname(this.filePath); const fileName = basename(this.filePath); const resultName = fileName.replace(".csv", ".bin"); @@ -173,7 +175,7 @@ export class NVSPartitionTable { Logger.errorNotify( "nvs_partition_gen.py is not defined", new Error( - "nvs_partition_gen.py is not defined, Make sure idf.espIdfPath is correct." + "nvs_partition_gen.py is not defined, Make sure IDF_PATH in your setup is correct." ), "NVSPartitionTable generateNvsPartition, toolsPath" ); diff --git a/src/espIdf/openOcd/boardConfiguration.ts b/src/espIdf/openOcd/boardConfiguration.ts index f00eaff33..a60e50ea1 100644 --- a/src/espIdf/openOcd/boardConfiguration.ts +++ b/src/espIdf/openOcd/boardConfiguration.ts @@ -21,7 +21,6 @@ import { readJSON } from "fs-extra"; import { Logger } from "../../logger/logger"; import { commands, ConfigurationTarget, l10n, Uri, window } from "vscode"; import { defaultBoards } from "./defaultBoards"; -import { IdfToolsManager } from "../../idfToolsManager"; import { getIdfTargetFromSdkconfig } from "../../workspaceConfig"; export interface IdfBoard { @@ -32,22 +31,13 @@ export interface IdfBoard { } export async function getOpenOcdScripts(workspace: Uri): Promise { - const idfPathDir = readParameter("idf.espIdfPath", workspace) as string; - const toolsPath = readParameter("idf.toolsPath", workspace) as string; - const userExtraVars = readParameter("idf.customExtraVars", workspace) as { - [key: string]: string; - }; - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - idfPathDir - ); - const idfExtraVars = await idfToolsManager.exportVars( - join(toolsPath, "tools") - ); + const userExtraVars = readParameter( + "idf.customExtraVars", + workspace + ) as { [key: string]: string }; let openOcdScriptsPath: string; try { - openOcdScriptsPath = idfExtraVars.hasOwnProperty("OPENOCD_SCRIPTS") - ? idfExtraVars.OPENOCD_SCRIPTS - : userExtraVars.hasOwnProperty("OPENOCD_SCRIPTS") + openOcdScriptsPath = userExtraVars.hasOwnProperty("OPENOCD_SCRIPTS") ? userExtraVars.OPENOCD_SCRIPTS : process.env.OPENOCD_SCRIPTS ? process.env.OPENOCD_SCRIPTS diff --git a/src/espIdf/openOcd/openOcdManager.ts b/src/espIdf/openOcd/openOcdManager.ts index d01e0c0b6..654128e94 100644 --- a/src/espIdf/openOcd/openOcdManager.ts +++ b/src/espIdf/openOcd/openOcdManager.ts @@ -23,7 +23,6 @@ import * as idfConf from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { OutputChannel } from "../../logger/outputChannel"; import { - appendIdfAndToolsToPath, isBinInPath, PreCheck, spawn as sspawn, @@ -35,6 +34,7 @@ import { CommandKeys, createCommandDictionary, } from "../../cmdTreeView/cmdStore"; +import { configureEnvVariables } from "../../common/prepareEnv"; export interface IOpenOCDConfig { workspace: vscode.Uri; @@ -60,7 +60,7 @@ export class OpenOCDManager extends EventEmitter { } public async version(): Promise { - const modifiedEnv = await appendIdfAndToolsToPath(this.workspace); + const modifiedEnv = await configureEnvVariables(this.workspace); const openOcdPath = await isBinInPath("openocd", modifiedEnv, [ "openocd-esp32", ]); @@ -159,7 +159,7 @@ export class OpenOCDManager extends EventEmitter { if (this.isRunning()) { return; } - const modifiedEnv = await appendIdfAndToolsToPath(this.workspace); + const modifiedEnv = await configureEnvVariables(this.workspace); const openOcdPath = await isBinInPath("openocd", modifiedEnv, [ "openocd-esp32", ]); diff --git a/src/espIdf/partition-table/partitionFlasher.ts b/src/espIdf/partition-table/partitionFlasher.ts index db5917e86..77fd92b13 100644 --- a/src/espIdf/partition-table/partitionFlasher.ts +++ b/src/espIdf/partition-table/partitionFlasher.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Monday, 19th July 2021 7:11:49 pm * Copyright 2021 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,8 +24,9 @@ import { readSerialPort, } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; -import { appendIdfAndToolsToPath, spawn } from "../../utils"; +import { spawn } from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; export async function flashBinaryToPartition( offset: string, @@ -49,7 +50,7 @@ export async function flashBinaryToPartition( }, async (progress: Progress<{ message: string; increment: number }>) => { try { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const modifiedEnv = await configureEnvVariables(workspaceFolder); const serialPort = await readSerialPort(workspaceFolder, false); if (!serialPort) { return Logger.warnNotify( @@ -59,8 +60,8 @@ export async function flashBinaryToPartition( ) ); } - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const idfPath = modifiedEnv["IDF_PATH"]; + const pythonBinPath = await getVirtualEnvPythonPath(); const esptoolPath = join( idfPath, "components", diff --git a/src/espIdf/partition-table/partitionReader.ts b/src/espIdf/partition-table/partitionReader.ts index 3d4b70063..dff8ba661 100644 --- a/src/espIdf/partition-table/partitionReader.ts +++ b/src/espIdf/partition-table/partitionReader.ts @@ -19,9 +19,10 @@ import { dirname, join } from "path"; import { l10n, Progress, ProgressLocation, Uri, window } from "vscode"; import { NotificationMode, readParameter, readSerialPort } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; -import { appendIdfAndToolsToPath, spawn } from "../../utils"; +import { spawn } from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { ensureDir } from "fs-extra"; +import { configureEnvVariables } from "../../common/prepareEnv"; export async function readPartition( name: string, @@ -46,7 +47,7 @@ export async function readPartition( }, async (progress: Progress<{ message: string; increment: number }>) => { try { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const modifiedEnv = await configureEnvVariables(this.config.workspaceFolder); const serialPort = await readSerialPort(workspaceFolder, false); if (!serialPort) { return Logger.warnNotify( @@ -56,8 +57,8 @@ export async function readPartition( ) ); } - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const idfPath = modifiedEnv.IDF_PATH; + const pythonBinPath = await getVirtualEnvPythonPath(); const esptoolPath = join( idfPath, "components", diff --git a/src/espIdf/partition-table/tree.ts b/src/espIdf/partition-table/tree.ts index a39bc2732..adcb18396 100644 --- a/src/espIdf/partition-table/tree.ts +++ b/src/espIdf/partition-table/tree.ts @@ -17,7 +17,6 @@ */ import { ensureDir, pathExists, readFile, stat } from "fs-extra"; -import { EOL } from "os"; import { join } from "path"; import { Disposable, @@ -29,20 +28,15 @@ import { TreeItem, Uri, window, - workspace, } from "vscode"; import { readParameter, readSerialPort } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; -import { - appendIdfAndToolsToPath, - getConfigValueFromSDKConfig, - PreCheck, - spawn, -} from "../../utils"; import { CSV2JSON } from "../../views/partition-table/util"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { createFlashModel } from "../../flash/flashModelBuilder"; import { formatAsPartitionSize } from "./partitionReader"; +import { spawn } from "../../utils"; +import { configureEnvVariables } from "../../common/prepareEnv"; export class PartitionItem extends TreeItem { name: string; @@ -84,10 +78,7 @@ export class PartitionTreeDataProvider public async populatePartitionItems(workspace: Uri) { this.partitionItems = Array(0); try { - const idfPath = readParameter("idf.espIdfPath", workspace); - const buildPath = readParameter("idf.buildPath", workspace); - const flashBaudRate = readParameter("idf.flashBaudRate", workspace); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); + const modifiedEnv = await configureEnvVariables(workspace); const serialPort = readParameter("idf.port", workspace) as string; if (!serialPort) { return Logger.warnNotify( @@ -97,7 +88,10 @@ export class PartitionTreeDataProvider ) ); } - const pythonBinPath = await getVirtualEnvPythonPath(workspace); + const buildPath = readParameter("idf.buildPath", workspace); + const flashBaudRate = readParameter("idf.flashBaudRate", workspace); + const idfPath = modifiedEnv["IDF_PATH"]; + const pythonBinPath = await getVirtualEnvPythonPath(); const flasherArgsPath = join(buildPath, "flasher_args.json"); diff --git a/src/espIdf/reconfigure/task.ts b/src/espIdf/reconfigure/task.ts index 2e3c666d1..03bc16809 100644 --- a/src/espIdf/reconfigure/task.ts +++ b/src/espIdf/reconfigure/task.ts @@ -27,24 +27,23 @@ import { workspace, } from "vscode"; import { NotificationMode, readParameter } from "../../idfConfiguration"; -import { appendIdfAndToolsToPath, getSDKConfigFilePath } from "../../utils"; +import { getSDKConfigFilePath } from "../../utils"; import { join } from "path"; import { TaskManager } from "../../taskManager"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { configureEnvVariables } from "../../common/prepareEnv"; export class IdfReconfigureTask { private buildDirPath: string; private curWorkspace: Uri; - private idfPathDir: string; constructor(workspace: Uri) { this.curWorkspace = workspace; - this.idfPathDir = readParameter("idf.espIdfPath", workspace) as string; this.buildDirPath = readParameter("idf.buildPath", workspace) as string; } public async reconfigure() { - const modifiedEnv = await appendIdfAndToolsToPath(this.curWorkspace); + const modifiedEnv = await configureEnvVariables(this.curWorkspace); const options: ProcessExecutionOptions = { cwd: this.curWorkspace.fsPath, env: modifiedEnv, @@ -63,7 +62,7 @@ export class IdfReconfigureTask { ? TaskRevealKind.Always : TaskRevealKind.Silent; - const idfPy = join(this.idfPathDir, "tools", "idf.py"); + const idfPy = join(modifiedEnv["IDF_PATH"], "tools", "idf.py"); const reconfigureArgs = [idfPy]; let buildPathArgsIndex = reconfigureArgs.indexOf("-B"); @@ -102,7 +101,7 @@ export class IdfReconfigureTask { reconfigureArgs.push("reconfigure"); - const pythonBinPath = await getVirtualEnvPythonPath(this.curWorkspace); + const pythonBinPath = await getVirtualEnvPythonPath(); const reconfigureExecution = new ProcessExecution( pythonBinPath, diff --git a/src/espIdf/serial/serialPort.ts b/src/espIdf/serial/serialPort.ts index ac0ae756e..cfe811e58 100644 --- a/src/espIdf/serial/serialPort.ts +++ b/src/espIdf/serial/serialPort.ts @@ -27,6 +27,7 @@ import * as SerialPortLib from "serialport"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { getIdfTargetFromSdkconfig } from "../../workspaceConfig"; import { showInfoNotificationWithAction } from "../../logger/utils"; +import { ESP } from "../../config"; export class SerialPort { /** @@ -98,7 +99,7 @@ export class SerialPort { "idf.espIdfPath", workspaceFolder ) as string; - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); const esptoolPath = join( idfPath, "components", @@ -365,11 +366,11 @@ export class SerialPort { ); }); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); - const idfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceFolder - ); + const pythonBinPath = await getVirtualEnvPythonPath(); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; const enableSerialPortChipIdRequest = idfConf.readParameter( "idf.enableSerialPortChipIdRequest", workspaceFolder diff --git a/src/espIdf/setTarget/DevkitsCommand.ts b/src/espIdf/setTarget/DevkitsCommand.ts index fd906de5e..99541b719 100644 --- a/src/espIdf/setTarget/DevkitsCommand.ts +++ b/src/espIdf/setTarget/DevkitsCommand.ts @@ -17,27 +17,28 @@ */ import * as vscode from "vscode"; -import * as fs from "fs" +import * as fs from "fs"; import { join } from "path"; import * as idfConf from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { OpenOCDManager } from "../openOcd/openOcdManager"; -import { appendIdfAndToolsToPath, execChildProcess } from "../../utils"; +import { execChildProcess } from "../../utils"; import { OutputChannel } from "../../logger/outputChannel"; import { getOpenOcdScripts } from "../openOcd/boardConfiguration"; +import { configureEnvVariables } from "../../common/prepareEnv"; export class DevkitsCommand { - private workspaceRoot: vscode.Uri; + private workspaceFolderUri: vscode.Uri; - constructor(workspaceRoot: vscode.Uri) { - this.workspaceRoot = workspaceRoot; + constructor(workspaceFolder: vscode.Uri) { + this.workspaceFolderUri = workspaceFolder; } public async runDevkitsScript(): Promise { try { const workspaceFolder = vscode.workspace.getWorkspaceFolder( - this.workspaceRoot + this.workspaceFolderUri ); if (!workspaceFolder) { throw new Error("No workspace folder found"); @@ -45,7 +46,7 @@ export class DevkitsCommand { const toolsPath = idfConf.readParameter( "idf.toolsPath", - this.workspaceRoot + this.workspaceFolderUri ) as string; const openOCDManager = OpenOCDManager.init(); const openOCDVersion = await openOCDManager.version(); @@ -67,7 +68,9 @@ export class DevkitsCommand { "esp_detect_config.py" ); - const openOcdScriptsPath = await getOpenOcdScripts(this.workspaceRoot); + const openOcdScriptsPath = await getOpenOcdScripts( + this.workspaceFolderUri + ); if (!openOcdScriptsPath) { throw new Error("Could not get OpenOCD scripts path"); } @@ -76,7 +79,7 @@ export class DevkitsCommand { const notificationMode = idfConf.readParameter( "idf.notificationMode", - this.workspaceRoot + this.workspaceFolderUri ) as string; const ProgressLocation = @@ -85,8 +88,8 @@ export class DevkitsCommand { ? vscode.ProgressLocation.Notification : vscode.ProgressLocation.Window; - const pythonBinPath = await getVirtualEnvPythonPath(this.workspaceRoot); - const modifiedEnv = await appendIdfAndToolsToPath(this.workspaceRoot); + const pythonBinPath = await getVirtualEnvPythonPath(); + const modifiedEnv = await configureEnvVariables(this.workspaceFolderUri); OutputChannel.init(); OutputChannel.appendLine( @@ -109,7 +112,7 @@ export class DevkitsCommand { const result = await execChildProcess( pythonBinPath, [scriptPath, "--esp-config", espConfigPath], - this.workspaceRoot.fsPath, + this.workspaceFolderUri.fsPath, OutputChannel.init(), { env: modifiedEnv }, cancelToken @@ -144,7 +147,7 @@ export class DevkitsCommand { try { const toolsPath = idfConf.readParameter( "idf.toolsPath", - this.workspaceRoot + this.workspaceFolderUri ) as string; const openOCDManager = OpenOCDManager.init(); const openOCDVersion = await openOCDManager.version(); diff --git a/src/espIdf/setTarget/getTargets.ts b/src/espIdf/setTarget/getTargets.ts index 39106baf2..26d1728c0 100644 --- a/src/espIdf/setTarget/getTargets.ts +++ b/src/espIdf/setTarget/getTargets.ts @@ -18,8 +18,8 @@ import { existsSync, readFileSync } from "fs"; import { join } from "path"; import { Uri } from "vscode"; -import { readParameter } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; +import { configureEnvVariables } from "../../common/prepareEnv"; export interface IdfTarget { label: string; @@ -32,9 +32,10 @@ export async function getTargetsFromEspIdf( workspaceFolder: Uri, givenIdfPathDir?: string ) { + const modifiedEnv = await configureEnvVariables(workspaceFolder); const idfPathDir = givenIdfPathDir ? givenIdfPathDir - : readParameter("idf.espIdfPath", workspaceFolder); + : modifiedEnv["IDF_PATH"]; const resultTargetArray: IdfTarget[] = []; try { diff --git a/src/espIdf/setTarget/setTargetInIdf.ts b/src/espIdf/setTarget/setTargetInIdf.ts index f42aac1d4..3786057b9 100644 --- a/src/espIdf/setTarget/setTargetInIdf.ts +++ b/src/espIdf/setTarget/setTargetInIdf.ts @@ -22,7 +22,6 @@ import { readParameter } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { OutputChannel } from "../../logger/outputChannel"; import { - appendIdfAndToolsToPath, setCCppPropertiesJsonCompilerPath, spawn, } from "../../utils"; @@ -30,6 +29,7 @@ import { ConfserverProcess } from "../menuconfig/confServerProcess"; import { IdfTarget } from "./getTargets"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import * as vscode from "vscode"; +import { configureEnvVariables } from "../../common/prepareEnv"; export async function setTargetInIDF( workspaceFolder: WorkspaceFolder, @@ -39,13 +39,12 @@ export async function setTargetInIDF( if (ConfserverProcess.exists()) { ConfserverProcess.dispose(); } - const idfPathDir = readParameter("idf.espIdfPath", workspaceFolder.uri); const buildDirPath = readParameter( "idf.buildPath", workspaceFolder.uri ) as string; - const idfPy = join(idfPathDir, "tools", "idf.py"); - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder.uri); + const modifiedEnv = await configureEnvVariables(workspaceFolder.uri); + const idfPy = join(modifiedEnv["IDF_PATH"], "tools", "idf.py"); modifiedEnv.IDF_TARGET = undefined; const enableCCache = readParameter( "idf.enableCCache", @@ -74,7 +73,7 @@ export async function setTargetInIDF( } setTargetArgs.push("set-target", selectedTarget.target); - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder.uri); + const pythonBinPath = await getVirtualEnvPythonPath(); OutputChannel.appendLine("Running IDF Set Target action", "Set Target"); const setTargetResult = await spawn(pythonBinPath, setTargetArgs, { cwd: workspaceFolder.uri.fsPath, diff --git a/src/espIdf/size/idfSize.ts b/src/espIdf/size/idfSize.ts index 8becd00af..5c1c7fda1 100644 --- a/src/espIdf/size/idfSize.ts +++ b/src/espIdf/size/idfSize.ts @@ -24,6 +24,7 @@ import { fileExists, spawn } from "../../utils"; import { getProjectName } from "../../workspaceConfig"; import * as utils from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { ESP } from "../../config"; export class IDFSize { private readonly workspaceFolderUri: vscode.Uri; @@ -54,16 +55,13 @@ export class IDFSize { const mapFilePath = await this.mapFilePath(); let locMsg = vscode.l10n.t("Gathering Overview"); - const espIdfPath = idfConf.readParameter( - "idf.espIdfPath", - this.workspaceFolderUri - ) as string; + const espIdfPath = this.idfPath(); const version = await utils.getEspIdfFromCMake(espIdfPath); const formatArgs = utils.compareVersion(version, "5.3.0") >= 0 ? ["--format", "json2"] : utils.compareVersion(version, "5.1.0") >= 0 - ? ["--format", "json"] + ? ["--format", "json"] : ["--json"]; const overview = await this.idfCommandInvoker([ "idf_size.py", @@ -106,11 +104,10 @@ export class IDFSize { } private idfPath(): string { - const idfPathDir = idfConf.readParameter( - "idf.espIdfPath", - this.workspaceFolderUri - ); - return path.join(idfPathDir, "tools"); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + return currentEnvVars["IDF_PATH"]; } public async isBuiltAlready() { @@ -120,11 +117,9 @@ export class IDFSize { private async idfCommandInvoker(args: string[]) { const idfPath = this.idfPath(); try { - const pythonBinPath = await getVirtualEnvPythonPath( - this.workspaceFolderUri - ); + const pythonBinPath = await getVirtualEnvPythonPath(); const buffOut = await spawn(pythonBinPath, args, { - cwd: idfPath, + cwd: path.join(idfPath, "tools"), }); const buffStr = buffOut.toString(); const buffObj = JSON.parse(buffStr); diff --git a/src/espIdf/size/idfSizeTask.ts b/src/espIdf/size/idfSizeTask.ts index f3087acdf..85c798853 100644 --- a/src/espIdf/size/idfSizeTask.ts +++ b/src/espIdf/size/idfSizeTask.ts @@ -28,9 +28,11 @@ import { } from "vscode"; import { NotificationMode, readParameter } from "../../idfConfiguration"; import { TaskManager } from "../../taskManager"; -import { appendIdfAndToolsToPath, readProjectCMakeLists } from "../../utils"; +import { readProjectCMakeLists } from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { OutputCapturingExecution } from "../../taskManager/customExecution"; +import { configureEnvVariables } from "../../common/prepareEnv"; +import { ESP } from "../../config"; export class IdfSizeTask { private currentWorkspace: Uri; @@ -39,8 +41,11 @@ export class IdfSizeTask { constructor(workspaceUri: Uri) { this.currentWorkspace = workspaceUri; - const idfPathDir = readParameter("idf.espIdfPath", workspaceUri) as string; - this.idfSizePath = join(idfPathDir, "tools", "idf_size.py"); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPath = currentEnvVars["IDF_PATH"]; + this.idfSizePath = join(idfPath, "tools", "idf_size.py"); this.buildDirPath = readParameter("idf.buildPath", workspaceUri) as string; } @@ -51,11 +56,11 @@ export class IdfSizeTask { public async getSizeInfo() { await ensureDir(this.buildDirPath); - const pythonCommand = await getVirtualEnvPythonPath(this.currentWorkspace); - const mapFilePath = this.mapFilePath(); + const pythonCommand = await getVirtualEnvPythonPath(); + const mapFilePath = await this.mapFilePath(); const args = [this.idfSizePath, mapFilePath]; - const modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); + const modifiedEnv = await configureEnvVariables(this.currentWorkspace); const processOptions = { cwd: this.buildDirPath, env: modifiedEnv, diff --git a/src/espIdf/tracing/gdbHeapTraceManager.ts b/src/espIdf/tracing/gdbHeapTraceManager.ts index 228e97cf2..6c8d37ca6 100644 --- a/src/espIdf/tracing/gdbHeapTraceManager.ts +++ b/src/espIdf/tracing/gdbHeapTraceManager.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Wednesday, 14th July 2021 2:51:09 pm * Copyright 2021 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,12 +22,7 @@ import { env, Uri, window } from "vscode"; import { readParameter } from "../../idfConfiguration"; import { Logger } from "../../logger/logger"; import { OutputChannel } from "../../logger/outputChannel"; -import { - appendIdfAndToolsToPath, - getToolchainToolName, - isBinInPath, - PreCheck, -} from "../../utils"; +import { getToolchainToolName, isBinInPath } from "../../utils"; import { getProjectName } from "../../workspaceConfig"; import { OpenOCDManager } from "../openOcd/openOcdManager"; import { AppTraceArchiveTreeDataProvider } from "./tree/appTraceArchiveTreeDataProvider"; @@ -35,13 +30,13 @@ import { AppTraceButtonType, AppTraceTreeDataProvider, } from "./tree/appTraceTreeDataProvider"; +import { configureEnvVariables } from "../../common/prepareEnv"; export class GdbHeapTraceManager { private treeDataProvider: AppTraceTreeDataProvider; private archiveDataProvider: AppTraceArchiveTreeDataProvider; private childProcess: ChildProcess; private gdbinitFileName: string = "heaptrace-gdbinit"; - private workspace: Uri; constructor( treeDataProvider: AppTraceTreeDataProvider, @@ -71,7 +66,7 @@ export class GdbHeapTraceManager { throw new Error(`${buildDirPath} doesn't exist. Build first.`); } await this.createGdbinitFile(fileName, buildDirPath); - const modifiedEnv = await appendIdfAndToolsToPath(workspace); + const modifiedEnv = await configureEnvVariables(workspace); const idfTarget = modifiedEnv.IDF_TARGET || "esp32"; const gdbTool = getToolchainToolName(idfTarget, "gdb"); const isGdbToolInPath = await isBinInPath( @@ -108,14 +103,22 @@ export class GdbHeapTraceManager { }); this.childProcess.on("error", (err) => { - Logger.errorNotify(err.message, err, "GdbHeapTraceManager start error"); + Logger.errorNotify( + err.message, + err, + "GdbHeapTraceManager start error" + ); this.stop(); }); this.childProcess.on("exit", (code, signal) => { if (code && code !== 0) { const errMsg = `Heap tracing process exited with code ${code} and signal ${signal}`; - Logger.errorNotify(errMsg, new Error(errMsg), "GdbHeapTraceManager start exit"); + Logger.errorNotify( + errMsg, + new Error(errMsg), + "GdbHeapTraceManager start exit" + ); } }); } diff --git a/src/espIdf/tracing/tools/abstractTracingToolManager.ts b/src/espIdf/tracing/tools/abstractTracingToolManager.ts index bce8de76c..7103f7b96 100644 --- a/src/espIdf/tracing/tools/abstractTracingToolManager.ts +++ b/src/espIdf/tracing/tools/abstractTracingToolManager.ts @@ -20,7 +20,9 @@ import { join } from "path"; import * as vscode from "vscode"; import * as idfConf from "../../../idfConfiguration"; -import { appendIdfAndToolsToPath, canAccessFile, spawn } from "../../../utils"; +import { canAccessFile, spawn } from "../../../utils"; +import { configureEnvVariables } from "../../../common/prepareEnv"; +import { ESP } from "../../../config"; export abstract class AbstractTracingToolManager { protected readonly traceFilePath: string; @@ -42,13 +44,16 @@ export abstract class AbstractTracingToolManager { args?: string[], option?: any ) { - const modifiedEnv = await appendIdfAndToolsToPath(this.workspaceRoot); + const modifiedEnv = await configureEnvVariables(this.workspaceRoot); option.env = option.env || modifiedEnv; return await spawn(command, args, option); } protected appTraceToolsPath(): string { - const idfPathDir = idfConf.readParameter("idf.espIdfPath", this.workspaceRoot); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPathDir = currentEnvVars["IDF_PATH"]; return join(idfPathDir, "tools", "esp_app_trace"); } diff --git a/src/espIdf/tracing/tools/xtensa/abstractXtensaTools.ts b/src/espIdf/tracing/tools/xtensa/abstractXtensaTools.ts index 43e530718..e51ae68fe 100644 --- a/src/espIdf/tracing/tools/xtensa/abstractXtensaTools.ts +++ b/src/espIdf/tracing/tools/xtensa/abstractXtensaTools.ts @@ -18,8 +18,9 @@ import * as vscode from "vscode"; import { Logger } from "../../../../logger/logger"; -import { appendIdfAndToolsToPath, getToolchainToolName, spawn } from "../../../../utils"; +import { getToolchainToolName, spawn } from "../../../../utils"; import { getIdfTargetFromSdkconfig } from "../../../../workspaceConfig"; +import { configureEnvVariables } from "../../../../common/prepareEnv"; export abstract class XtensaTools { protected readonly workspaceRoot: vscode.Uri; @@ -29,7 +30,7 @@ export abstract class XtensaTools { } protected async call(args: string[]): Promise { - const env = await appendIdfAndToolsToPath(this.workspaceRoot); + const env = await configureEnvVariables(this.workspaceRoot); const toolName = await this.toolNameForTarget(this.toolName); try { return await spawn(toolName, args, { env }); diff --git a/src/espIdf/unitTest/configure.ts b/src/espIdf/unitTest/configure.ts index f6a33e0a9..cc1fb457b 100644 --- a/src/espIdf/unitTest/configure.ts +++ b/src/espIdf/unitTest/configure.ts @@ -129,10 +129,10 @@ export async function flashTestApp( ); } const flashBaudRate = readParameter("idf.flashBaudRate", unitTestAppDirPath); - const idfPathDir = readParameter( - "idf.espIdfPath", - unitTestAppDirPath - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfPathDir = currentEnvVars["IDF_PATH"]; const canFlash = await verifyCanFlash( flashBaudRate, port, diff --git a/src/espMatter/espMatterDownload.ts b/src/espMatter/espMatterDownload.ts index 0c3166085..e53aae0e1 100644 --- a/src/espMatter/espMatterDownload.ts +++ b/src/espMatter/espMatterDownload.ts @@ -38,7 +38,7 @@ import { Logger } from "../logger/logger"; import { TaskManager } from "../taskManager"; import { OutputChannel } from "../logger/outputChannel"; import { PackageProgress } from "../PackageProgress"; -import { getVirtualEnvPythonPath, installEspMatterPyReqs } from "../pythonManager"; +import { installEspMatterPyReqs } from "../pythonManager"; import { platform } from "os"; export class EspMatterCloning extends AbstractCloning { @@ -64,10 +64,11 @@ export class EspMatterCloning extends AbstractCloning { if (EspMatterCloning.isBuildingGn) { throw new Error("ALREADY_BUILDING"); } - const matterPathDir = readParameter( - "idf.espMatterPath", + const customExtraVars = readParameter( + "idf.customExtraVars", this.currWorkspace - ); + ) as { [key: string]: string }; + const matterPathDir = customExtraVars["ESP_MATTER_PATH"]; const espMatterPathExists = await pathExists(matterPathDir); if (!espMatterPathExists) { return; @@ -182,7 +183,11 @@ export class EspMatterCloning extends AbstractCloning { ); } catch (error) { OutputChannel.appendLine(error.message); - Logger.errorNotify(error.message, error, "EspMatterCloning initEsp32PlatformSubmodules"); + Logger.errorNotify( + error.message, + error, + "EspMatterCloning initEsp32PlatformSubmodules" + ); } } ); @@ -259,15 +264,6 @@ export async function installPythonReqs( espMatterPath: string, workspace?: Uri ) { - const espIdfPath = readParameter("idf.espIdfPath", workspace); - const pythonBinPath = await getVirtualEnvPythonPath(workspace); - const containerPath = - process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; - const confToolsPath = readParameter("idf.toolsPath", workspace); - const toolsPath = - confToolsPath || - process.env.IDF_TOOLS_PATH || - join(containerPath, ".espressif"); const notificationMode = readParameter( "idf.notificationMode", workspace @@ -291,10 +287,8 @@ export async function installPythonReqs( message: `Installing Python Requirements...`, }); await installEspMatterPyReqs( - espIdfPath, - toolsPath, + workspace, espMatterPath, - pythonBinPath, undefined, cancelToken ); @@ -323,16 +317,18 @@ export async function getEspMatter(workspace?: Uri) { try { if (installAllSubmodules.target === "true") { - await espMatterInstaller.getRepository("idf.espMatterPath", workspace); - espMatterPath = readParameter("idf.espMatterPath", workspace); + await espMatterInstaller.getRepository("ESP_MATTER_PATH", workspace); + const customExtraVars = readParameter("idf.customExtraVars", workspace); + espMatterPath = customExtraVars["ESP_MATTER_PATH"]; await espMatterInstaller.startBootstrap(); } else { await espMatterInstaller.getRepository( - "idf.espMatterPath", + "ESP_MATTER_PATH", workspace, false ); - espMatterPath = readParameter("idf.espMatterPath", workspace); + const customExtraVars = readParameter("idf.customExtraVars", workspace); + espMatterPath = customExtraVars["ESP_MATTER_PATH"]; await espMatterInstaller.getSubmodules(espMatterPath); await espMatterInstaller.initEsp32PlatformSubmodules( espMatterPath, diff --git a/src/espMdf/espMdfDownload.ts b/src/espMdf/espMdfDownload.ts index f70863615..c5748bbac 100644 --- a/src/espMdf/espMdfDownload.ts +++ b/src/espMdf/espMdfDownload.ts @@ -30,5 +30,5 @@ export class MdfCloning extends AbstractCloning { export async function getEspMdf(workspace: Uri) { const gitPath = await readParameter("idf.gitPath", workspace) || "git"; const adfInstaller = new MdfCloning(gitPath); - await adfInstaller.getRepository("idf.espMdfPath", workspace); + await adfInstaller.getRepository("MDF_PATH", workspace); } diff --git a/src/extension.ts b/src/extension.ts index ab439d9db..eefff5ad2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,10 +16,6 @@ import * as path from "path"; import * as vscode from "vscode"; import { srcOp, UpdateCmakeLists } from "./cmake/srcsWatcher"; -import { - DebugAdapterManager, - IDebugAdapterConfig, -} from "./espIdf/debugAdapter/debugAdapterManager"; import { ConfserverProcess } from "./espIdf/menuconfig/confServerProcess"; import { IOpenOCDConfig, @@ -40,10 +36,7 @@ import { AppTraceTreeDataProvider } from "./espIdf/tracing/tree/appTraceTreeData import * as idfConf from "./idfConfiguration"; import { Logger } from "./logger/logger"; import { OutputChannel } from "./logger/outputChannel"; -import { - showInfoNotificationWithAction, - showInfoNotificationWithMultipleActions, -} from "./logger/utils"; +import { showInfoNotificationWithAction } from "./logger/utils"; import * as utils from "./utils"; import { PreCheck, shouldDisableMonitorReset } from "./utils"; import { @@ -78,16 +71,12 @@ import { ESPEFuseManager } from "./efuse"; import { constants, createFileSync, pathExists, readFile } from "fs-extra"; import { getEspAdf } from "./espAdf/espAdfDownload"; import { getEspMdf } from "./espMdf/espMdfDownload"; -import { SetupPanel } from "./setup/SetupPanel"; import { ChangelogViewer } from "./changelog-viewer"; import { PreReleaseNotification } from "./preReleaseNotification"; -import { getSetupInitialValues, ISetupInitArgs } from "./setup/setupInit"; import { getVirtualEnvPythonPath, installEspMatterPyReqs, - installExtensionPyReqs, } from "./pythonManager"; -import { checkExtensionSettings } from "./checkExtensionSettings"; import { CmakeListsEditorPanel } from "./cmake/cmakeEditorPanel"; import { seachInEspDocs } from "./espIdf/documentation/getSearchResults"; import { @@ -118,7 +107,6 @@ import { createNewIdfMonitor } from "./espIdf/monitor/command"; import { KconfigLangClient } from "./kconfig"; import { configureProjectWithGcov } from "./coverage/configureProject"; import { ComponentManagerUIPanel } from "./component-manager/panel"; -import { verifyAppBinary } from "./espIdf/debugAdapter/verifyApp"; import { QemuLaunchMode, QemuManager } from "./qemu/qemuManager"; import { PartitionItem, @@ -142,11 +130,6 @@ import { PeripheralBaseNode } from "./espIdf/debugAdapter/nodes/base"; import { ExtensionConfigStore } from "./common/store"; import { projectConfigurationPanel } from "./project-conf/projectConfPanel"; import { ProjectConfigStore } from "./project-conf"; -import { - clearPreviousIdfSetups, - getPreviousIdfSetups, - loadIdfSetupsFromEspIdfJson, -} from "./setup/existingIdfSetups"; import { getEspRainmaker } from "./rainmaker/download/espRainmakerDownload"; import { UnitTest } from "./espIdf/unitTest/adapter"; import { @@ -159,14 +142,12 @@ import { getFileList, getTestComponents } from "./espIdf/unitTest/utils"; import { saveDefSdkconfig } from "./espIdf/menuconfig/saveDefConfig"; import { createSBOM, installEspSBOM } from "./espBom"; import { getEspHomeKitSdk } from "./espHomekit/espHomekitDownload"; -import { getCurrentIdfSetup, selectIdfSetup } from "./versionSwitcher"; -import { checkDebugAdapterRequirements } from "./espIdf/debugAdapter/checkPyReqs"; +import { selectIdfSetup } from "./versionSwitcher"; import { CDTDebugConfigurationProvider } from "./cdtDebugAdapter/debugConfProvider"; import { CDTDebugAdapterDescriptorFactory } from "./cdtDebugAdapter/server"; import { IdfReconfigureTask } from "./espIdf/reconfigure/task"; import { ErrorHintProvider, - ErrorHintTreeItem, HintHoverProvider, } from "./espIdf/hints/index"; import { installWebsocketClient } from "./espIdf/monitor/checkWebsocketClient"; @@ -177,7 +158,6 @@ import { createCommandDictionary, IDFWebCommandKeys, } from "./cmdTreeView/cmdStore"; -import { IdfSetup } from "./views/setup/types"; import { asyncRemoveEspIdfSettings } from "./uninstall"; import { clearSelectedProjectConfiguration, @@ -194,6 +174,13 @@ import { OpenOCDErrorMonitor } from "./espIdf/hints/openocdhint"; import { updateHintsStatusBarItem } from "./statusBar"; import { activateLanguageTool, deactivateLanguageTool } from "./langTools"; import { readSerialPort } from "./idfConfiguration"; +import { getIdfSetups } from "./eim/getExistingSetups"; +import { loadIdfSetup } from "./eim/loadIdfSetup"; +import { configureEnvVariables } from "./common/prepareEnv"; +import { + downloadExtractAndRunEIM, + runExistingEIM, +} from "./eim/downloadInstall"; // Global variables shared by commands let workspaceRoot: vscode.Uri; @@ -205,7 +192,6 @@ let covRenderer: CoverageRenderer; let openOCDManager: OpenOCDManager; let isOpenOCDLaunchedByDebug: boolean = false; let isDebugRestarted: boolean = false; -let debugAdapterManager: DebugAdapterManager; // QEMU let qemuManager: QemuManager; @@ -269,12 +255,11 @@ const minIdfVersionCheck = async function ( minVersion: string, workspace: vscode.Uri ) { - const espIdfPath = idfConf.readParameter( - "idf.espIdfPath", - workspace - ) as string; - const gitPath = idfConf.readParameter("idf.gitPath", workspace) || "git"; - const currentVersion = await utils.getEspIdfFromCMake(espIdfPath); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let currentVersion = ""; + currentVersion = await utils.getEspIdfFromCMake(currentEnvVars["IDF_PATH"]); return [ () => PreCheck.espIdfVersionValidator(minVersion, currentVersion), `Selected command needs ESP-IDF v${minVersion} or higher`, @@ -356,8 +341,6 @@ export async function activate(context: vscode.ExtensionContext) { ); } } - - debugAdapterManager = DebugAdapterManager.init(context); OutputChannel.init(); const registerIDFCommand = ( name: string, @@ -423,6 +406,7 @@ export async function activate(context: vscode.ExtensionContext) { registerQemuStatusBarItem(context); if (PreCheck.isWorkspaceFolderOpen()) { + await loadIdfSetup(vscode.workspace.workspaceFolders[0].uri); await createCmdsStatusBarItems(vscode.workspace.workspaceFolders[0].uri); workspaceRoot = initSelectedWorkspace(statusBarItems["workspace"]); ESP.GlobalConfiguration.store.set( @@ -485,6 +469,7 @@ export async function activate(context: vscode.ExtensionContext) { for (const ws of e.removed) { if (workspaceRoot && ws.uri === workspaceRoot) { workspaceRoot = initSelectedWorkspace(statusBarItems["workspace"]); + await loadIdfSetup(workspaceRoot); ESP.GlobalConfiguration.store.set( ESP.GlobalConfiguration.SELECTED_WORKSPACE_FOLDER, workspaceRoot @@ -525,17 +510,18 @@ export async function activate(context: vscode.ExtensionContext) { ESP.ProjectConfiguration.SELECTED_CONFIG ); } - const currentIdfSetup = await getCurrentIdfSetup( - workspaceRoot, - false + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + const idfVersion = await utils.getEspIdfFromCMake( + currentEnvVars["IDF_PATH"] ); - context.subscriptions.push(projectConfigManager); if (statusBarItems["currentIdfVersion"]) { - statusBarItems["currentIdfVersion"].text = currentIdfSetup.isValid + statusBarItems["currentIdfVersion"].text = idfVersion ? `$(${ commandDictionary[CommandKeys.SelectCurrentIdfVersion] .iconId - }) ESP-IDF v${currentIdfSetup.version}` + }) ESP-IDF v${idfVersion}` : `$(${ commandDictionary[CommandKeys.SelectCurrentIdfVersion] .iconId @@ -548,6 +534,7 @@ export async function activate(context: vscode.ExtensionContext) { } if (typeof workspaceRoot === "undefined") { workspaceRoot = initSelectedWorkspace(statusBarItems["workspace"]); + await loadIdfSetup(workspaceRoot); ESP.GlobalConfiguration.store.set( ESP.GlobalConfiguration.SELECTED_WORKSPACE_FOLDER, workspaceRoot @@ -565,11 +552,6 @@ export async function activate(context: vscode.ExtensionContext) { ) as string; const projectName = await getProjectName(buildDirPath); const projectElfFile = `${path.join(buildDirPath, projectName)}.elf`; - const debugAdapterConfig = { - currentWorkspace: workspaceRoot, - elfFile: projectElfFile, - } as IDebugAdapterConfig; - debugAdapterManager.configureAdapter(debugAdapterConfig); const openOCDConfig: IOpenOCDConfig = { workspace: workspaceRoot, } as IOpenOCDConfig; @@ -594,7 +576,6 @@ export async function activate(context: vscode.ExtensionContext) { isOpenOCDLaunchedByDebug = false; openOCDManager.stop(); } - debugAdapterManager.stop(); }); const sdkconfigWatcher = vscode.workspace.createFileSystemWatcher( @@ -771,10 +752,10 @@ export async function activate(context: vscode.ExtensionContext) { const gitPath = (await idfConf.readParameter("idf.gitPath", workspaceRoot)) || "git"; - const idfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let idfPath = currentEnvVars["IDF_PATH"]; const arduinoComponentManager = new ArduinoComponentInstaller( idfPath, workspaceRoot.fsPath, @@ -987,6 +968,7 @@ export async function activate(context: vscode.ExtensionContext) { ESP.GlobalConfiguration.SELECTED_WORKSPACE_FOLDER, workspaceRoot ); + await loadIdfSetup(workspaceRoot); await getIdfTargetFromSdkconfig( workspaceRoot, statusBarItems["target"] @@ -1030,20 +1012,22 @@ export async function activate(context: vscode.ExtensionContext) { ESP.ProjectConfiguration.SELECTED_CONFIG ); } - const currentIdfSetup = await getCurrentIdfSetup(workspaceRoot, false); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + + const idfVersion = await utils.getEspIdfFromCMake( + currentEnvVars["IDF_PATH"] + ); if (statusBarItems["currentIdfVersion"]) { - statusBarItems["currentIdfVersion"].text = currentIdfSetup.isValid + statusBarItems["currentIdfVersion"].text = idfVersion ? `$(${ commandDictionary[CommandKeys.SelectCurrentIdfVersion].iconId - }) ESP-IDF v${currentIdfSetup.version}` + }) ESP-IDF v${idfVersion}` : `$(${ commandDictionary[CommandKeys.SelectCurrentIdfVersion].iconId }) ESP-IDF InvalidSetup`; } - const debugAdapterConfig = { - currentWorkspace: workspaceRoot, - } as IDebugAdapterConfig; - debugAdapterManager.configureAdapter(debugAdapterConfig); const openOCDConfig: IOpenOCDConfig = { workspace: workspaceRoot, } as IOpenOCDConfig; @@ -1117,10 +1101,6 @@ export async function activate(context: vscode.ExtensionContext) { ); }); - registerIDFCommand("espIdf.clearSavedIdfSetups", async () => { - await clearPreviousIdfSetups(); - }); - registerIDFCommand("espIdf.rmProjectConfStatusBar", async () => { if (statusBarItems["projectConf"]) { statusBarItems["projectConf"].dispose(); @@ -1212,7 +1192,7 @@ export async function activate(context: vscode.ExtensionContext) { statusBarItems[statusItem] = undefined; } } - } else if (e.affectsConfiguration("idf.espIdfPath" + winFlag)) { + } else if (e.affectsConfiguration("idf.customExtraVars")) { ESP.URL.Docs.IDF_INDEX = undefined; } else if (e.affectsConfiguration("idf.port" + winFlag)) { if (statusBarItems && statusBarItems["port"]) { @@ -1275,11 +1255,6 @@ export async function activate(context: vscode.ExtensionContext) { } }); - const debugProvider = new IdfDebugConfigurationProvider(); - context.subscriptions.push( - vscode.debug.registerDebugConfigurationProvider("espidf", debugProvider) - ); - const cdtDebugProvider = new CDTDebugConfigurationProvider(); context.subscriptions.push( vscode.debug.registerDebugConfigurationProvider( @@ -1296,73 +1271,6 @@ export async function activate(context: vscode.ExtensionContext) { ) ); - vscode.debug.registerDebugAdapterDescriptorFactory("espidf", { - async createDebugAdapterDescriptor(session: vscode.DebugSession) { - try { - const portToUse = session.configuration.debugPort || DEBUG_DEFAULT_PORT; - const launchMode = session.configuration.mode || "auto"; - const useMonitorWithDebug = idfConf.readParameter( - "idf.launchMonitorOnDebugSession", - workspaceRoot - ); - if ( - session.configuration.sessionID !== "core-dump.debug.session.ws" && - useMonitorWithDebug - ) { - await createNewIdfMonitor(workspaceRoot, true); - } - if ( - launchMode === "auto" && - !openOCDManager.isRunning() && - session.configuration.sessionID !== "core-dump.debug.session.ws" - ) { - isOpenOCDLaunchedByDebug = true; - await openOCDManager.start(); - } - if (session.configuration.sessionID === "qemu.debug.session") { - const debugAdapterConfig = { - appOffset: session.configuration.appOffset, - elfFile: session.configuration.elfFilePath, - debugAdapterPort: portToUse, - tmoScaleFactor: session.configuration.tmoScaleFactor, - } as IDebugAdapterConfig; - debugAdapterManager.configureAdapter(debugAdapterConfig); - await debugAdapterManager.start(); - } - if (session.configuration.sessionID === "core-dump.debug.session.ws") { - await debugAdapterManager.start(); - } - if (launchMode === "auto" && !debugAdapterManager.isRunning()) { - const debugAdapterConfig = { - appOffset: session.configuration.appOffset, - debugAdapterPort: portToUse, - elfFile: session.configuration.elfFilePath, - env: session.configuration.env, - gdbinitFilePath: session.configuration.gdbinitFile, - initGdbCommands: session.configuration.initGdbCommands || [], - tmoScaleFactor: session.configuration.tmoScaleFactor, - isPostMortemDebugMode: false, - isOocdDisabled: false, - logLevel: session.configuration.logLevel, - } as IDebugAdapterConfig; - debugAdapterManager.configureAdapter(debugAdapterConfig); - await debugAdapterManager.start(); - } - return new vscode.DebugAdapterServer(portToUse); - } catch (error) { - const errMsg = - error && error.message - ? error.message - : "Error starting ESP-IDF Debug Adapter"; - return Logger.errorNotify( - errMsg, - error, - "extension createDebugAdapterDescriptor espidf" - ); - } - }, - }); - vscode.debug.onDidStartDebugSession(async (session) => { const svdFile = idfConf.readParameter( "idf.svdFilePath", @@ -1604,72 +1512,6 @@ export async function activate(context: vscode.ExtensionContext) { ); }); - registerIDFCommand("espIdf.installPyReqs", () => { - return PreCheck.perform([openFolderCheck], async () => { - const notificationMode = idfConf.readParameter( - "idf.notificationMode", - workspaceRoot - ) as string; - const ProgressLocation = - notificationMode === idfConf.NotificationMode.All || - notificationMode === idfConf.NotificationMode.Notifications - ? vscode.ProgressLocation.Notification - : vscode.ProgressLocation.Window; - vscode.window.withProgress( - { - cancellable: true, - location: ProgressLocation, - title: "ESP-IDF: Installing Python requirements", - }, - async ( - progress: vscode.Progress<{ message: string; increment?: number }>, - cancelToken: vscode.CancellationToken - ) => { - try { - const espIdfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; - const containerPath = - process.platform === "win32" - ? process.env.USERPROFILE - : process.env.HOME; - const confToolsPath = idfConf.readParameter( - "idf.toolsPath", - workspaceRoot - ) as string; - const toolsPath = - confToolsPath || - process.env.IDF_TOOLS_PATH || - path.join(containerPath, ".espressif"); - const pyPath = await getVirtualEnvPythonPath(workspaceRoot); - progress.report({ - message: vscode.l10n.t( - `Installing ESP-IDF extension Python requirements...` - ), - }); - await installExtensionPyReqs( - pyPath, - espIdfPath, - toolsPath, - undefined - ); - vscode.window.showInformationMessage( - vscode.l10n.t("ESP-IDF Python Requirements has been installed") - ); - } catch (error) { - const msg = error.message - ? error.message - : typeof error === "string" - ? error - : "Error installing Python requirements"; - Logger.errorNotify(msg, error, "extension installPyReqs"); - } - } - ); - }); - }); - registerIDFCommand("espIdf.installEspMatterPyReqs", () => { if (process.platform === "win32") { return vscode.window.showInformationMessage( @@ -1697,37 +1539,19 @@ export async function activate(context: vscode.ExtensionContext) { cancelToken: vscode.CancellationToken ) => { try { - const espIdfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; - const containerPath = - process.platform === "win32" - ? process.env.USERPROFILE - : process.env.HOME; - const confToolsPath = idfConf.readParameter( - "idf.toolsPath", - workspaceRoot - ) as string; - const toolsPath = - confToolsPath || - process.env.IDF_TOOLS_PATH || - path.join(containerPath, ".espressif"); - const espMatterPath = idfConf.readParameter( - "idf.espMatterPath", + const customExtraVars = idfConf.readParameter( + "idf.customExtraVars", workspaceRoot - ) as string; - const pyPath = await getVirtualEnvPythonPath(workspaceRoot); + ) as { [key: string]: string }; + const espMatterPath = customExtraVars["ESP_MATTER_PATH"]; progress.report({ message: vscode.l10n.t( `Installing ESP-Matter Python Requirements...` ), }); await installEspMatterPyReqs( - espIdfPath, - toolsPath, + workspaceRoot, espMatterPath, - pyPath, undefined, cancelToken ); @@ -2281,54 +2105,55 @@ export async function activate(context: vscode.ExtensionContext) { }); }); - registerIDFCommand("espIdf.setup.start", (setupArgs?: ISetupInitArgs) => { - PreCheck.perform([webIdeCheck, isNotDockerContainerCheck], async () => { - try { - if (SetupPanel.isCreatedAndHidden()) { - SetupPanel.createOrShow(context); - return; - } - const notificationMode = idfConf.readParameter( - "idf.notificationMode", - workspaceRoot - ) as string; - const ProgressLocation = - notificationMode === idfConf.NotificationMode.All || - notificationMode === idfConf.NotificationMode.Notifications - ? vscode.ProgressLocation.Notification - : vscode.ProgressLocation.Window; - await vscode.window.withProgress( - { - cancellable: false, - location: ProgressLocation, - title: vscode.l10n.t("ESP-IDF: Configure ESP-IDF extension"), - }, - async ( - progress: vscode.Progress<{ message: string; increment: number }> - ) => { - try { - setupArgs = setupArgs - ? setupArgs - : await getSetupInitialValues( - context.extensionPath, - progress, - workspaceRoot - ); - setupArgs.espIdfStatusBar = statusBarItems["currentIdfVersion"]; - SetupPanel.createOrShow(context, setupArgs); - } catch (error) { - Logger.errorNotify( - error.message, - error, - "extension setup getSetupInitialValues" - ); + registerIDFCommand("espIdf.installManager", async () => { + const notificationMode = idfConf.readParameter( + "idf.notificationMode", + workspaceRoot + ) as string; + const ProgressLocation = + notificationMode === idfConf.NotificationMode.All || + notificationMode === idfConf.NotificationMode.Notifications + ? vscode.ProgressLocation.Notification + : vscode.ProgressLocation.Window; + vscode.window.withProgress( + { + cancellable: true, + location: ProgressLocation, + title: vscode.l10n.t("ESP-IDF Install Manager"), + }, + async ( + progress: vscode.Progress<{ message: string; increment: number }>, + cancelToken: vscode.CancellationToken + ) => { + const doesEIMExecutableExist = await runExistingEIM( + progress, + cancelToken + ); + if (!doesEIMExecutableExist) { + progress.report({ + message: `EIM executable not found. Please choose a download mirror.`, + increment: 0, + }); + const mirrorToUse = await vscode.window.showQuickPick( + ["Github", "Espressif (faster in China)", "Open Releases URL"], + { + placeHolder: vscode.l10n.t("Select mirror to use"), } + ); + if (mirrorToUse === "Open Releases URL") { + vscode.env.openExternal( + vscode.Uri.parse(ESP.URL.InstallManager.Releases) + ); + return; } - ); - } catch (error) { - Logger.errorNotify(error.message, error, "extension setup"); + let useMirror = false; + if (mirrorToUse && mirrorToUse === "Espressif (faster in China)") { + useMirror = true; + } + await downloadExtractAndRunEIM(progress, cancelToken, useMirror); + } } - }); + ); }); registerIDFCommand( @@ -2375,10 +2200,7 @@ export async function activate(context: vscode.ExtensionContext) { cancelToken: vscode.CancellationToken ) => { try { - const welcomeArgs = await getWelcomePageInitialValues( - progress, - workspaceRoot - ); + const welcomeArgs = await getWelcomePageInitialValues(progress); if (!welcomeArgs) { throw new Error("Error getting welcome page initial values"); } @@ -2390,7 +2212,7 @@ export async function activate(context: vscode.ExtensionContext) { ); }); - registerIDFCommand("espIdf.newProject.start", () => { + registerIDFCommand("espIdf.newProject.start", async () => { if (NewProjectPanel.isCreatedAndHidden()) { NewProjectPanel.createOrShow(context.extensionPath); return; @@ -2404,6 +2226,21 @@ export async function activate(context: vscode.ExtensionContext) { notificationMode === idfConf.NotificationMode.Notifications ? vscode.ProgressLocation.Notification : vscode.ProgressLocation.Window; + + let idfSetups = await getIdfSetups(workspaceRoot); + const currentIdfSetup = await loadIdfSetup(workspaceRoot); + const isCurrentSetupInList = idfSetups.findIndex((idfSetup) => { + return ( + idfSetup.idfPath === currentIdfSetup.idfPath && + idfSetup.toolsPath === currentIdfSetup.toolsPath + ); + }); + if (isCurrentSetupInList === -1) { + idfSetups.push(currentIdfSetup); + } + if (idfSetups.length === 0) { + return; + } vscode.window.withProgress( { cancellable: false, @@ -2418,7 +2255,8 @@ export async function activate(context: vscode.ExtensionContext) { const newProjectArgs = await getNewProjectArgs( context.extensionPath, progress, - workspaceRoot + workspaceRoot, + idfSetups ); if (newProjectArgs) { NewProjectPanel.createOrShow(context.extensionPath, newProjectArgs); @@ -2916,7 +2754,7 @@ export async function activate(context: vscode.ExtensionContext) { { cancellable: false, location: ProgressLocation, - title: vscode.l10n.t("ESP-IDF: Preparing ESP-IDF extension report"), + title: vscode.l10n.t("ESP-IDF Doctor"), }, async ( progress: vscode.Progress<{ message: string; increment: number }> @@ -2926,7 +2764,8 @@ export async function activate(context: vscode.ExtensionContext) { await generateConfigurationReport( context, workspaceRoot, - reportedResult + reportedResult, + progress ); await vscode.window.showTextDocument( vscode.Uri.file(path.join(context.extensionPath, "report.txt")) @@ -3081,13 +2920,17 @@ export async function activate(context: vscode.ExtensionContext) { } // For Heap Trace, show the webview as before + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let espIdfPath = currentEnvVars["IDF_PATH"]; AppTracePanel.createOrShow(context, { trace: { fileName: trace.fileName, filePath: trace.filePath, type: trace.type, workspacePath: workspaceRoot.fsPath, - idfPath: idfConf.readParameter("idf.espIdfPath", workspaceRoot), + idfPath: espIdfPath, }, }); }); @@ -3351,7 +3194,7 @@ export async function activate(context: vscode.ExtensionContext) { ); } - const pythonBinPath = await getVirtualEnvPythonPath(workspaceRoot); + const pythonBinPath = await getVirtualEnvPythonPath(); if (!utils.canAccessFile(pythonBinPath, constants.R_OK)) { Logger.errorNotify( vscode.l10n.t("Python binary path is not defined"), @@ -3359,10 +3202,10 @@ export async function activate(context: vscode.ExtensionContext) { "extension launchWSServerAndMonitor python path not set" ); } - const idfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let idfPath = currentEnvVars["IDF_PATH"]; const idfMonitorToolPath = path.join( idfPath, "tools", @@ -3742,7 +3585,7 @@ export async function activate(context: vscode.ExtensionContext) { }, async () => { try { - const pythonBinPath = await getVirtualEnvPythonPath(workspaceRoot); + const pythonBinPath = await getVirtualEnvPythonPath(); const ninjaSummaryScript = path.join( context.extensionPath, "external", @@ -3894,7 +3737,6 @@ export async function activate(context: vscode.ExtensionContext) { Logger.warn(`Failed to handle URI Open, ${uri.toString()}`); }, }); - checkExtensionSettings(workspaceRoot, statusBarItems["currentIdfVersion"]); // WALK-THROUGH let disposable = vscode.commands.registerCommand( @@ -4483,10 +4325,10 @@ async function startFlashing( } return await jtagFlashCommand(workspaceRoot); } else { - const idfPathDir = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let idfPathDir = currentEnvVars["IDF_PATH"]; return await flashCommand( cancelToken, flashBaudRate, @@ -4506,30 +4348,27 @@ async function createEspIdfTerminal( initialCommand?: string, location?: vscode.TerminalLocation ): Promise { - const modifiedEnv = await utils.appendIdfAndToolsToPath(workspaceRoot); + const shellExecutableArgs = idfConf.readParameter( + "idf.customTerminalExecutableArgs", + workspaceRoot + ) as string[]; + const modifiedEnv = await configureEnvVariables(workspaceRoot); let shellArgs = []; if (process.platform === "win32") { - if ( - vscode.env.shell.indexOf("powershell") !== -1 || - vscode.env.shell.indexOf("pwsh") !== -1 - ) { - shellArgs = ["-ExecutionPolicy", "Bypass"]; - } + shellArgs = ["-ExecutionPolicy", "Bypass"]; + } else if (shellExecutableArgs && shellExecutableArgs.length) { + shellArgs = shellExecutableArgs; } let shellExecutablePath = idfConf.readParameter( "idf.customTerminalExecutable", workspaceRoot ) as string; - const shellExecutableArgs = idfConf.readParameter( - "idf.customTerminalExecutableArgs", - workspaceRoot - ) as string[]; - if (!shellExecutablePath) { - shellExecutablePath = vscode.env.shell; - } - if (shellExecutableArgs && shellExecutableArgs.length) { - shellArgs = shellExecutableArgs; - } + const shellPath = + process.platform === "win32" + ? "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + : shellExecutablePath + ? shellExecutablePath + : vscode.env.shell; const espIdfTerminal = vscode.window.createTerminal({ name: terminalName, @@ -4537,23 +4376,24 @@ async function createEspIdfTerminal( cwd: workspaceRoot.fsPath || modifiedEnv.IDF_PATH || process.cwd(), strictEnv: true, shellArgs, - shellPath: shellExecutablePath, + shellPath, location, }); + const currentSetup = await loadIdfSetup(workspaceRoot); + const activationScriptPathExists = await pathExists( + currentSetup.activationScript + ); + if (process.platform === "win32") { - if (shellExecutablePath.indexOf("cmd.exe") !== -1) { - espIdfTerminal.sendText( - `call "${path.join(extensionPath, "export.bat")}"` - ); - } else if ( - shellExecutablePath.indexOf("powershell") !== -1 || - shellExecutablePath.indexOf("pwsh") !== -1 - ) { - espIdfTerminal.sendText( - `& '${path.join(extensionPath, "export.ps1").replace(/'/g, "''")}'` - ); - } + const activationScriptPath = activationScriptPathExists + ? currentSetup.activationScript + : path.join(extensionPath, "export.ps1"); + espIdfTerminal.sendText(`& '${activationScriptPath.replace(/'/g, "''")}'`); + } else if (activationScriptPathExists) { + espIdfTerminal.sendText( + `. '${currentSetup.activationScript.replace(/'/g, "''")}'` + ); } if (initialCommand) { @@ -4716,69 +4556,3 @@ export function deactivate() { KconfigLangClient.stopKconfigLangServer(); deactivateLanguageTool(); } - -class IdfDebugConfigurationProvider - implements vscode.DebugConfigurationProvider { - public async resolveDebugConfiguration( - folder: vscode.WorkspaceFolder | undefined, - config: vscode.DebugConfiguration, - token?: vscode.CancellationToken - ): Promise { - try { - const buildDirPath = idfConf.readParameter( - "idf.buildPath", - workspaceRoot - ) as string; - const projectName = await getProjectName(buildDirPath); - const elfFilePath = path.join(buildDirPath, `${projectName}.elf`); - const elfFileExists = await pathExists(elfFilePath); - if (!elfFileExists) { - throw new Error( - `${elfFilePath} doesn't exist. Build this project first.` - ); - } - if (config.verifyAppBinBeforeDebug) { - const isSameAppBinary = await verifyAppBinary(workspaceRoot); - if (!isSameAppBinary) { - throw new Error( - vscode.l10n.t( - `Current app binary is different from your project. Flash first.` - ) - ); - } - } - config.elfFilePath = elfFilePath; - const debugAdapterPackagesExist = await checkDebugAdapterRequirements( - workspaceRoot - ); - if (!debugAdapterPackagesExist) { - const installDAPyPkgs = await vscode.window.showInformationMessage( - "ESP-IDF Debug Adapter Python packages are not installed", - "Install" - ); - if (installDAPyPkgs && installDAPyPkgs === "Install") { - await vscode.commands.executeCommand("espIdf.installPyReqs"); - } - return; - } - } catch (error) { - const msg = error.message - ? error.message - : "Some build files doesn't exist. Build this project first."; - Logger.error( - error.message, - error, - "extension IdfDebugConfigurationProvider" - ); - const startBuild = await vscode.window.showInformationMessage( - msg, - "Build" - ); - if (startBuild === "Build") { - await buildFlashAndMonitor(false); - return; - } - } - return config; - } -} diff --git a/src/flash/dfu.ts b/src/flash/dfu.ts index b445b5878..03fac517d 100644 --- a/src/flash/dfu.ts +++ b/src/flash/dfu.ts @@ -16,8 +16,9 @@ * limitations under the License. */ import * as vscode from "vscode"; -import { appendIdfAndToolsToPath, execChildProcess } from "../utils"; +import { execChildProcess } from "../utils"; import { OutputChannel } from "../logger/outputChannel"; +import { configureEnvVariables } from "../common/prepareEnv"; function deviceLabel(selectedDevice: string) { const regex = new RegExp(/:\d+\]/g); @@ -30,7 +31,7 @@ function deviceLabel(selectedDevice: string) { } export async function getDfuList(workspaceUri: vscode.Uri) { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceUri); + const modifiedEnv = await configureEnvVariables(workspaceUri); const dfuListStr = await execChildProcess( "dfu-util", ["--list"], diff --git a/src/flash/flashTask.ts b/src/flash/flashTask.ts index 1fe8f61bb..02e8e7729 100644 --- a/src/flash/flashTask.ts +++ b/src/flash/flashTask.ts @@ -21,12 +21,13 @@ import { join } from "path"; import * as vscode from "vscode"; import * as idfConf from "../idfConfiguration"; import { FlashModel } from "./flashModel"; -import { appendIdfAndToolsToPath, canAccessFile } from "../utils"; +import { canAccessFile } from "../utils"; import { TaskManager } from "../taskManager"; import { getDfuList, selectDfuDevice, selectedDFUAdapterId } from "./dfu"; import { ESP } from "../config"; import { getVirtualEnvPythonPath } from "../pythonManager"; import { OutputCapturingExecution } from "../taskManager/customExecution"; +import { configureEnvVariables } from "../common/prepareEnv"; export class FlashTask { public static isFlashing: boolean; @@ -59,10 +60,10 @@ export class FlashTask { workspaceUri ) as string; this.encryptPartitions = encryptPartitions; - this.idfPathDir = idfConf.readParameter( - "idf.espIdfPath", - this.currentWorkspace - ) as string; + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + this.idfPathDir = currentEnvVars["IDF_PATH"]; } public flashing(flag: boolean) { @@ -94,7 +95,7 @@ export class FlashTask { "idf.notificationMode", this.currentWorkspace ) as string; - const pythonBinPath = await getVirtualEnvPythonPath(this.currentWorkspace); + const pythonBinPath = await getVirtualEnvPythonPath(); const currentWorkspaceFolder = vscode.workspace.workspaceFolders.find( (w) => w.uri === this.currentWorkspace ); @@ -104,7 +105,7 @@ export class FlashTask { ? vscode.TaskRevealKind.Always : vscode.TaskRevealKind.Silent; let flashExecution: OutputCapturingExecution; - this.modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); + this.modifiedEnv = await configureEnvVariables(this.currentWorkspace); this.processOptions = { cwd: this.buildDirPath, env: this.modifiedEnv, diff --git a/src/idfComponentsDataProvider.ts b/src/idfComponentsDataProvider.ts index 5b074e1d6..717afb5a0 100644 --- a/src/idfComponentsDataProvider.ts +++ b/src/idfComponentsDataProvider.ts @@ -19,6 +19,7 @@ import * as idfConf from "./idfConfiguration"; import { Logger } from "./logger/logger"; import * as utils from "./utils"; import { join } from "path"; +import { ESP } from "./config"; export class IdfTreeDataProvider implements TreeDataProvider { private OnDidChangeTreeData: EventEmitter< @@ -79,10 +80,10 @@ export class IdfTreeDataProvider implements TreeDataProvider { utils.readFileSync(this.projectDescriptionJsonPath) ); - const defaultComponentsDir = idfConf.readParameter( - "idf.espIdfPath", - this.workspaceFolder - ); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + let defaultComponentsDir = currentEnvVars["IDF_PATH"]; if ( Object.prototype.hasOwnProperty.call( diff --git a/src/idfConfiguration.ts b/src/idfConfiguration.ts index 8f4da0710..5565b1484 100644 --- a/src/idfConfiguration.ts +++ b/src/idfConfiguration.ts @@ -345,6 +345,12 @@ export function resolveVariables( if (Object.keys(customExtraVars).indexOf(envVariable) !== -1) { return customExtraVars[envVariable]; } + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION); + if (Object.keys(currentEnvVars).indexOf(envVariable) !== -1) { + return currentEnvVars[envVariable]; + } if (Object.keys(process.env).indexOf(envVariable) === -1) { return ""; } diff --git a/src/idfToolsManager.ts b/src/idfToolsManager.ts index 52b46fec0..d88e7cc2d 100644 --- a/src/idfToolsManager.ts +++ b/src/idfToolsManager.ts @@ -29,13 +29,8 @@ export interface IEspIdfTool { doesToolExist: boolean; env: {}; expected: string; - hashResult: boolean; - hasFailed: boolean; - id: string; name: string; path: string; - progress: string; - progressDetail: string; } export class IdfToolsManager { @@ -383,13 +378,45 @@ export class IdfToolsManager { doesToolExist: isToolVersionCorrect, env: pkgVars, expected: expectedVersions.join(","), - hashResult: isToolVersionCorrect, - hasFailed: false, - id: pkg.name, name: pkg.name, path: pkgExportedPath, - progress: "0.00%", - progressDetail: "", + } as IEspIdfTool; + }); + return idfToolsList; + } + + public async getEIMToolsInfo( + pathToVerify: string, + onReqPkgs?: string[], + logToChannel: boolean = true + ) { + let versions: { [key: string]: string } = await this.verifyPackages( + pathToVerify, + onReqPkgs, + logToChannel + ); + const packages = await this.getPackageList(onReqPkgs); + const idfToolsList = packages.map((pkg) => { + const pkgVersionsForPlatform = pkg.versions.filter((version) => { + return ( + Object.getOwnPropertyNames(version).indexOf( + this.platformInfo.platformToUse + ) > -1 || + Object.getOwnPropertyNames(version).indexOf( + this.platformInfo.fallbackPlatform + ) > -1 + ); + }); + const expectedVersions = pkgVersionsForPlatform.map((p) => p.name); + let isToolVersionCorrect = + expectedVersions.indexOf(versions[pkg.name]) > -1 || + (versions[pkg.name] && versions[pkg.name] === "No command version"); + return { + actual: versions[pkg.name] || "", + description: pkg.description, + doesToolExist: isToolVersionCorrect, + expected: expectedVersions.join(","), + name: pkg.name, } as IEspIdfTool; }); return idfToolsList; diff --git a/src/installManager.ts b/src/installManager.ts deleted file mode 100644 index bec986dfe..000000000 --- a/src/installManager.ts +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as del from "del"; -import * as fs from "fs"; -import { ensureDir, pathExists, remove } from "fs-extra"; -import * as path from "path"; -import * as tarfs from "tar-fs"; -import * as vscode from "vscode"; -import * as yauzl from "yauzl"; -import * as zlib from "zlib"; -import { IdfToolsManager } from "./idfToolsManager"; -import { IPackage } from "./IPackage"; -import { Logger } from "./logger/logger"; -import { OutputChannel } from "./logger/outputChannel"; -import { PackageError } from "./packageError"; -import { installEspIdfToolFromIdf } from "./pythonManager"; -import * as utils from "./utils"; - -export class InstallManager { - constructor(private installPath: string) {} - public getToolPackagesPath(toolPackage: string[]) { - return path.resolve(this.installPath, ...toolPackage); - } - - public async installPackages( - idfToolsManager: IdfToolsManager, - progress: vscode.Progress<{ message?: string; increment?: number }>, - pythonBinPath: string, - cancelToken?: vscode.CancellationToken, - onReqPkgs?: string[] - ): Promise { - return idfToolsManager.getPackageList(onReqPkgs).then((packages) => { - let count: number = 1; - return utils.buildPromiseChain(packages, async (pkg) => { - const versionName = idfToolsManager.getVersionToUse(pkg); - const absolutePath: string = this.getToolPackagesPath([ - "tools", - pkg.name, - versionName, - ]); - const binDir = pkg.binaries - ? path.join(absolutePath, ...pkg.binaries) - : absolutePath; - const toolPathExists = await pathExists(binDir); - if (toolPathExists) { - const binVersion = await idfToolsManager.checkBinariesVersion( - pkg, - binDir - ); - const expectedVersion = idfToolsManager.getVersionToUse(pkg); - if (binVersion === expectedVersion) { - this.appendChannel( - `Using existing ${pkg.description} in ${absolutePath}` - ); - progress.report({ - message: `Installed ${count}/${packages.length}: ${pkg.description}...`, - }); - return; - } - } - - progress.report({ - message: `Installing ${count}/${packages.length}: ${pkg.description}...`, - }); - this.appendChannel(`Installing package ${pkg.description}`); - const urlToUse = idfToolsManager.obtainUrlInfoForPlatform(pkg); - const parsedUrl = urlToUse.url.split(/\#|\?/)[0].split("."); // Get url file extension - let p: Promise; - if (parsedUrl[parsedUrl.length - 1] === "zip") { - p = this.installZipPackage( - idfToolsManager, - pkg, - absolutePath, - cancelToken - ).then(async () => { - if (pkg.strip_container_dirs) { - await this.promisedStripContainerDirs( - absolutePath, - pkg.strip_container_dirs - ); - } - }); - } else if ( - parsedUrl[parsedUrl.length - 2] === "tar" && - parsedUrl[parsedUrl.length - 1] === "gz" - ) { - p = this.installTarPackage( - idfToolsManager, - pkg, - absolutePath, - cancelToken - ).then(async () => { - if (pkg.strip_container_dirs) { - await this.promisedStripContainerDirs( - absolutePath, - pkg.strip_container_dirs - ); - } - }); - } else if ( - parsedUrl[parsedUrl.length - 2] === "tar" && - parsedUrl[parsedUrl.length - 1] === "xz" - ) { - p = this.installToolFromIdf( - idfToolsManager, - pythonBinPath, - pkg, - cancelToken - ).then(async () => { - if (pkg.strip_container_dirs) { - await this.promisedStripContainerDirs( - absolutePath, - pkg.strip_container_dirs - ); - } - }); - } else { - p = new Promise((resolve) => { - resolve(); - }); - } - count += 1; - return p; - }); - }); - } - - public async installZipFile( - zipFilePath: string, - destPath: string, - cancelToken?: vscode.CancellationToken - ) { - return new Promise(async (resolve, reject) => { - const doesZipFileExists = await pathExists(zipFilePath); - if (!doesZipFileExists) { - return reject(`File ${zipFilePath} doesn't exist.`); - } - yauzl.open(zipFilePath, { lazyEntries: true }, (error, zipfile) => { - if (error) { - return reject( - new PackageError("Zip file error", "InstallZipFile", error) - ); - } - if (cancelToken && cancelToken.isCancellationRequested) { - return reject( - new PackageError("Install cancelled by user", "InstallZipFile") - ); - } - - zipfile.on("end", () => { - return resolve(); - }); - zipfile.on("error", (err) => { - return reject( - new PackageError("Zip File error", "InstallZipFile", err) - ); - }); - - zipfile.readEntry(); - zipfile.on("entry", async (entry: yauzl.Entry) => { - const absolutePath: string = path.resolve(destPath, entry.fileName); - const dirExists = await utils.dirExistPromise(absolutePath); - if (dirExists) { - try { - await del(absolutePath, { force: true }); - } catch (err) { - this.appendChannel( - `Error deleting previous ${absolutePath}: ${err.message}` - ); - return reject( - new PackageError( - "Install folder cant be deleted", - "InstallZipFile", - err, - err.errorCode - ) - ); - } - } - if (entry.fileName.endsWith("/")) { - try { - await ensureDir(absolutePath, { mode: 0o775 }); - zipfile.readEntry(); - } catch (err) { - return reject( - new PackageError( - "Error creating directory", - "InstallZipFile", - err - ) - ); - } - } else { - const exists = await pathExists(absolutePath); - if (!exists) { - zipfile.openReadStream( - entry, - async (err, readStream: fs.ReadStream) => { - if (err) { - return reject( - new PackageError( - "Error reading zip stream", - "InstallZipFile", - err - ) - ); - } - readStream.on("error", (openErr) => { - return reject( - new PackageError( - "Error in readstream", - "InstallZipFile", - openErr - ) - ); - }); - - try { - await ensureDir(path.dirname(absolutePath), { - mode: 0o775, - }); - } catch (mkdirErr) { - return reject( - new PackageError( - "Error creating directory", - "InstallZipFile", - mkdirErr - ) - ); - } - const absoluteEntryTmpPath: string = absolutePath + ".tmp"; - const doesTmpPathExists = await pathExists( - absoluteEntryTmpPath - ); - if (doesTmpPathExists) { - try { - await remove(absoluteEntryTmpPath); - } catch (rmError) { - return reject( - new PackageError( - `Error unlinking tmp file ${absoluteEntryTmpPath}`, - "InstallZipFile", - rmError - ) - ); - } - } - const writeStream: fs.WriteStream = fs.createWriteStream( - absoluteEntryTmpPath, - { mode: 0o755 } - ); - writeStream.on("error", (writeStreamErr) => { - return reject( - new PackageError( - "Error in writeStream", - "InstallZipFile", - writeStreamErr - ) - ); - }); - writeStream.on("close", async () => { - try { - await utils.robustMove( - absoluteEntryTmpPath, - absolutePath - ); - } catch (closeWriteStreamErr) { - return reject( - new PackageError( - `Error renaming file ${absoluteEntryTmpPath}`, - "InstallZipFile", - closeWriteStreamErr - ) - ); - } - zipfile.readEntry(); - }); - readStream.pipe(writeStream); - } - ); - } else { - if (path.extname(absolutePath) !== ".txt") { - this.appendChannel( - `Warning File ${absolutePath} - already exists and was not updated.` - ); - } - zipfile.readEntry(); - } - } - }); - }); - }); - } - - public async installZipPackage( - idfToolsManager: IdfToolsManager, - pkg: IPackage, - absolutePath: string, - cancelToken?: vscode.CancellationToken - ) { - this.appendChannel(`Installing zip package ${pkg.description}`); - const urlInfo = idfToolsManager.obtainUrlInfoForPlatform(pkg); - const fileName = utils.fileNameFromUrl(urlInfo.url); - const packageFile: string = this.getToolPackagesPath(["dist", fileName]); - - const packageDownloaded = await pathExists(packageFile); - if (!packageDownloaded) { - this.appendChannel( - `${pkg.description} downloaded file is not available.` - ); - throw new PackageError( - "Error finding downloaded file", - "InstallZipPackage" - ); - } - const isValidFile = await utils.validateFileSizeAndChecksum( - packageFile, - urlInfo.sha256, - urlInfo.size - ); - if (!isValidFile) { - this.appendChannel( - `Package ${pkg.description} downloaded file is invalid.` - ); - throw new PackageError("Downloaded file invalid", "InstallZipPackage"); - } - return await this.installZipFile(packageFile, absolutePath, cancelToken); - } - - public installTarPackage( - idfToolsManager: IdfToolsManager, - pkg: IPackage, - absolutePath: string, - cancelToken?: vscode.CancellationToken - ): Promise { - return new Promise(async (resolve, reject) => { - const urlInfo = idfToolsManager.obtainUrlInfoForPlatform(pkg); - const fileName = utils.fileNameFromUrl(urlInfo.url); - const packageFile: string = this.getToolPackagesPath(["dist", fileName]); - - if (cancelToken && cancelToken.isCancellationRequested) { - return reject(new Error("Process cancelled by user")); - } - - const packageDownloaded = await pathExists(packageFile); - if (!packageDownloaded) { - this.appendChannel( - `Package ${pkg.description} downloaded file not available.` - ); - return reject( - new PackageError("Downloaded file unavailable", "InstallTarPackage") - ); - } - const isValidFile = await utils.validateFileSizeAndChecksum( - packageFile, - urlInfo.sha256, - urlInfo.size - ); - if (!isValidFile) { - this.appendChannel( - `Package ${pkg.description} downloaded file is invalid.` - ); - return reject( - new PackageError("Downloaded file invalid", "InstallTarPackage") - ); - } - const dirExists = await utils.dirExistPromise(absolutePath); - if (dirExists) { - try { - await del(absolutePath, { force: true }); - } catch (err) { - this.appendChannel( - `Error deleting ${pkg.name} old install: ${err.message}` - ); - return reject( - new PackageError( - "Install folder cant be deleted", - "installTarPackage", - err, - err.errorCode - ) - ); - } - } - const binPath = pkg.binaries - ? path.join(absolutePath, ...pkg.binaries, pkg.version_cmd[0]) - : path.join(absolutePath, pkg.version_cmd[0]); - - const binExists = await pathExists(binPath); - if (binExists) { - this.appendChannel( - `Existing ${pkg.description} found in ${this.installPath}` - ); - return resolve(); - } - this.appendChannel(`Installing tar.gz package ${pkg.description}`); - const extractor = tarfs.extract(absolutePath, { - readable: true, // all dirs and files should be readable - writable: true, // all dirs and files should be writable - }); - extractor.on("error", (err) => { - reject( - new PackageError( - "Extracting gunzipped tar error", - "InstallTarPackage", - err, - err.code - ) - ); - }); - extractor.on("finish", () => { - return resolve(); - }); - try { - fs.createReadStream(packageFile) - .pipe(zlib.createGunzip()) - .pipe(extractor); - } catch (error) { - return reject(error); - } - }); - } - - public installToolFromIdf( - idfToolsManager: IdfToolsManager, - pythonBinPath: string, - pkg: IPackage, - cancelToken?: vscode.CancellationToken - ) { - return installEspIdfToolFromIdf( - idfToolsManager.espIdfPath, - pythonBinPath, - this.installPath, - pkg.name, - cancelToken - ); - } - - private appendChannel(text: string): void { - OutputChannel.init().appendLine(text); - Logger.info(text); - } - - private async promisedStripContainerDirs(pkgDirPath: string, levels: number) { - const tmpPath = pkgDirPath + ".tmp"; - const exists = await utils.dirExistPromise(tmpPath); - if (exists) { - await del(tmpPath, { force: true }); - } - await utils.robustMove(pkgDirPath, tmpPath); - await ensureDir(pkgDirPath); - let basePath = tmpPath; - // Walk given number of levels down - for (let i = 0; i < levels; i++) { - const files = await utils.readDirPromise(basePath); - if (files.length > 1) { - throw new Error(`At level ${i} expected 1 entry, got ${files.length}`); - } - basePath = path.join(basePath, files[0]); - const isDirectory = await utils.dirExistPromise(basePath); - if (!isDirectory) { - throw new Error( - `At level ${levels[i]}, ${files[0]} is not a directory.` - ); - } - } - // Get list of directories/files to move - const filesToMove = await utils.readDirPromise(basePath); - for (let file of filesToMove) { - const moveFrom = path.join(basePath, file); - const moveTo = path.join(pkgDirPath, file); - await utils.robustMove(moveFrom, moveTo); - } - await del(tmpPath, { force: true }); - } -} diff --git a/src/logger/logger.ts b/src/logger/logger.ts index e7e7db8db..a96b85103 100644 --- a/src/logger/logger.ts +++ b/src/logger/logger.ts @@ -89,7 +89,7 @@ export class Logger { ...metadata, message: error.message, stack: error.stack, - category: category + category, }); } diff --git a/src/newProject/newProjectInit.ts b/src/newProject/newProjectInit.ts index 622c1faab..08a5bdc87 100644 --- a/src/newProject/newProjectInit.ts +++ b/src/newProject/newProjectInit.ts @@ -23,17 +23,12 @@ import { getOpenOcdScripts, IdfBoard, } from "../espIdf/openOcd/boardConfiguration"; -import { - getPreviousIdfSetups, - loadIdfSetupsFromEspIdfJson, -} from "../setup/existingIdfSetups"; -import { IdfSetup } from "../views/setup/types"; import { getTargetsFromEspIdf, IdfTarget, } from "../espIdf/setTarget/getTargets"; -import { getCurrentIdfSetup } from "../versionSwitcher"; import { join } from "path"; +import { IdfSetup } from "../eim/types"; export interface INewProjectArgs { espIdfSetup: IdfSetup; @@ -53,7 +48,8 @@ export interface INewProjectArgs { export async function getNewProjectArgs( extensionPath: string, progress: Progress<{ message: string; increment: number }>, - workspace: Uri + workspace: Uri, + idfSetups: IdfSetup[] ) { progress.report({ increment: 10, message: "Loading ESP-IDF components..." }); const components = []; @@ -76,39 +72,13 @@ export async function getNewProjectArgs( const openOcdScriptsPath = await getOpenOcdScripts(workspace); let espBoards = await getBoards(openOcdScriptsPath); progress.report({ increment: 10, message: "Loading ESP-IDF setups list..." }); - const idfSetups = await getPreviousIdfSetups(true); - const toolsPath = idfConf.readParameter("idf.toolsPath", workspace) as string; - let existingIdfSetups = await loadIdfSetupsFromEspIdfJson(toolsPath); - if (process.env.IDF_TOOLS_PATH && toolsPath !== process.env.IDF_TOOLS_PATH) { - const systemIdfSetups = await loadIdfSetupsFromEspIdfJson( - process.env.IDF_TOOLS_PATH - ); - existingIdfSetups = [...existingIdfSetups, ...systemIdfSetups]; - } - const currentIdfSetup = await getCurrentIdfSetup(workspace); - let setupsToUse = [...idfSetups, ...existingIdfSetups, currentIdfSetup]; - setupsToUse = setupsToUse.filter( - (setup, index, self) => - index === - self.findIndex( - (s) => s.idfPath === setup.idfPath && s.toolsPath === setup.toolsPath - ) - ); - if (setupsToUse.length === 0) { - await window.showInformationMessage("No ESP-IDF Setups found"); - return; - } - const onlyValidIdfSetups = [ - ...new Map( - setupsToUse.filter((i) => i.isValid).map((item) => [item.idfPath, item]) - ).values(), - ]; + const pickItems: { description: string; label: string; target: IdfSetup; }[] = []; - for (const idfSetup of onlyValidIdfSetups) { + for (const idfSetup of idfSetups) { pickItems.push({ description: `ESP-IDF v${idfSetup.version}`, label: l10n.t(`Use ESP-IDF {espIdfPath}`, { @@ -126,26 +96,15 @@ export async function getNewProjectArgs( return; } const idfSetup = espIdfPathToUse.target; - const espAdfPath = idfConf.readParameter( - "idf.espAdfPath", - workspace - ) as string; - const espMdfPath = idfConf.readParameter( - "idf.espMdfPath", - workspace - ) as string; - const espMatterPath = idfConf.readParameter( - "idf.espMatterPath", - workspace - ) as string; - const espHomeKitSdkPath = idfConf.readParameter( - "idf.espHomeKitSdkPath", - workspace - ) as string; - const espRainmakerPath = idfConf.readParameter( - "idf.espRainmakerPath", + const customExtraVars = idfConf.readParameter( + "idf.customExtraVars", workspace - ) as string; + ) as { [key: string]: string }; + const espAdfPath = customExtraVars["ADF_PATH"]; + const espMdfPath = customExtraVars["MDF_PATH"]; + const espMatterPath = customExtraVars["ESP_MATTER_PATH"]; + const espRainmakerPath = customExtraVars["RMAKER_PATH"]; + const espHomeKitSdkPath = customExtraVars["HOMEKIT_PATH"]; let templates: { [key: string]: IExampleCategory } = {}; const idfExists = await dirExistPromise(idfSetup.idfPath); if (idfExists) { diff --git a/src/newProject/newProjectPanel.ts b/src/newProject/newProjectPanel.ts index 55e5cddb9..5814b3003 100644 --- a/src/newProject/newProjectPanel.ts +++ b/src/newProject/newProjectPanel.ts @@ -22,8 +22,8 @@ import * as utils from "../utils"; import { IExample } from "../examples/Example"; import { setCurrentSettingsInTemplate } from "./utils"; import { NotificationMode, readParameter } from "../idfConfiguration"; -import { IdfSetup } from "../views/setup/types"; import { createClangdFile } from "../clang"; +import { IdfSetup } from "../eim/types"; export class NewProjectPanel { public static currentPanel: NewProjectPanel | undefined; @@ -304,8 +304,8 @@ export class NewProjectPanel { idfSetup, port, selectedIdfTarget, - openOcdConfigs, - vscode.Uri.file(newProjectPath) + vscode.Uri.file(newProjectPath), + openOcdConfigs ); await createClangdFile(vscode.Uri.file(newProjectPath)); await writeJSON(settingsJsonPath, settingsJson, { diff --git a/src/newProject/utils.ts b/src/newProject/utils.ts index 122caab6d..4835a9a96 100644 --- a/src/newProject/utils.ts +++ b/src/newProject/utils.ts @@ -15,44 +15,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { readParameter } from "../idfConfiguration"; import { readJSON } from "fs-extra"; -import { Uri } from "vscode"; -import { IdfSetup } from "../views/setup/types"; -import { getSystemPythonFromSettings } from "../pythonManager"; import { setClangSettings } from "../clang/index"; +import { IdfSetup } from "../eim/types"; +import { Uri } from "vscode"; export async function setCurrentSettingsInTemplate( settingsJsonPath: string, idfSetup: IdfSetup, port: string, selectedIdfTarget: string, - openOcdConfigs?: string, - workspace?: Uri + workspace: Uri, + openOcdConfigs?: string ) { const settingsJson = await readJSON(settingsJsonPath); - const adfPathDir = readParameter("idf.espAdfPath", workspace); - const mdfPathDir = readParameter("idf.espMdfPath", workspace); const isWin = process.platform === "win32" ? "Win" : ""; - if (idfSetup.idfPath) { - settingsJson["idf.espIdfPath" + isWin] = idfSetup.idfPath; - } - if (idfSetup.python) { - settingsJson["idf.pythonInstallPath"] = await getSystemPythonFromSettings( - idfSetup.python, - idfSetup.idfPath, - idfSetup.toolsPath - ); - } else { - settingsJson["idf.pythonInstallPath"] = idfSetup.sysPythonPath; - } - if (adfPathDir) { - settingsJson["idf.espAdfPath" + isWin] = adfPathDir; - } - if (mdfPathDir) { - settingsJson["idf.espMdfPath" + isWin] = mdfPathDir; - } if (openOcdConfigs) { settingsJson["idf.openOcdConfigs"] = openOcdConfigs.indexOf(",") !== -1 @@ -62,11 +39,12 @@ export async function setCurrentSettingsInTemplate( if (port.indexOf("no port") === -1) { settingsJson["idf.port" + isWin] = port; } - if (idfSetup.toolsPath) { - settingsJson["idf.toolsPath" + isWin] = idfSetup.toolsPath; + if (idfSetup.idfPath) { + settingsJson["idf.currentSetup"] = idfSetup.idfPath; } if (selectedIdfTarget) { - settingsJson["idf.customExtraVars"] = settingsJson["idf.customExtraVars"] || {}; + settingsJson["idf.customExtraVars"] = + settingsJson["idf.customExtraVars"] || {}; settingsJson["idf.customExtraVars"]["IDF_TARGET"] = selectedIdfTarget; } await setClangSettings(settingsJson, workspace); diff --git a/src/pythonManager.ts b/src/pythonManager.ts index a82efbfdc..877db1e15 100644 --- a/src/pythonManager.ts +++ b/src/pythonManager.ts @@ -13,20 +13,14 @@ // limitations under the License. import { PyReqLog } from "./PyReqLog"; -import { - CancellationToken, - ConfigurationTarget, - ExtensionContext, - Uri, -} from "vscode"; +import { CancellationToken, Uri } from "vscode"; import * as utils from "./utils"; -import { constants, pathExists } from "fs-extra"; +import { constants } from "fs-extra"; import { Logger } from "./logger/logger"; -import { delimiter, dirname, join, sep } from "path"; +import { join } from "path"; import { OutputChannel } from "./logger/outputChannel"; -import { readParameter, writeParameter } from "./idfConfiguration"; -import { ESP } from "./config"; import { EOL } from "os"; +import { ESP } from "./config"; export async function installEspIdfToolFromIdf( espDir: string, @@ -108,152 +102,17 @@ export async function getEnvVarsFromIdfTools( } } -export async function installPythonEnvFromIdfTools( - espDir: string, - idfToolsDir: string, - pyTracker: PyReqLog, - pythonBinPath: string, - gitPath: string, - context: ExtensionContext, - cancelToken?: CancellationToken, - pypiIndexUrl?: string -) { - const idfToolsPyPath = join(espDir, "tools", "idf_tools.py"); - const modifiedEnv: { [key: string]: string } = <{ [key: string]: string }>( - Object.assign({}, process.env) - ); - modifiedEnv.IDF_TOOLS_PATH = idfToolsDir; - modifiedEnv.IDF_PATH = espDir; - if (pypiIndexUrl) { - modifiedEnv.PIP_INDEX_URL = pypiIndexUrl; - } - if (process.platform === "win32") { - let pathToGitDir: string; - if (gitPath && gitPath !== "git") { - pathToGitDir = dirname(gitPath); - } - const pathNameInEnv: string = Object.keys(process.env).find( - (k) => k.toUpperCase() == "PATH" - ); - if (pathToGitDir) { - modifiedEnv[pathNameInEnv] = - pathToGitDir + delimiter + modifiedEnv[pathNameInEnv]; - } - modifiedEnv.PYTHONNOUSERSITE = "1"; - modifiedEnv[pathNameInEnv] = - dirname(pythonBinPath) + - sep + - "Lib" + - delimiter + - modifiedEnv[pathNameInEnv]; - const collection = context.environmentVariableCollection; - collection.prepend(pathNameInEnv, join(dirname(pythonBinPath), "Lib"), { - applyAtShellIntegration: true, - applyAtProcessCreation: true, - }); - } - - await execProcessWithLog( - pythonBinPath, - [idfToolsPyPath, "install-python-env"], - pyTracker, - { env: modifiedEnv, cwd: idfToolsDir, cancelToken } - ); - - const virtualEnvPython = await getPythonEnvPath( - espDir, - idfToolsDir, - pythonBinPath - ); - return virtualEnvPython; -} - -export async function installExtensionPyReqs( - virtualEnvPython: string, - espDir: string, - idfToolsDir: string, - pyTracker?: PyReqLog, - opts?: { - env: NodeJS.ProcessEnv; - cwd: string; - cancelToken?: CancellationToken; - } -) { - const reqDoesNotExists = " doesn't exist. Make sure the path is correct."; - const debugAdapterRequirements = join( - utils.extensionContext.extensionPath, - "esp_debug_adapter", - "requirements.txt" - ); - if (!utils.canAccessFile(debugAdapterRequirements, constants.R_OK)) { - Logger.warnNotify(debugAdapterRequirements + reqDoesNotExists); - OutputChannel.appendLine(debugAdapterRequirements + reqDoesNotExists); - return; - } - const fullEspIdfVersion = await utils.getEspIdfFromCMake(espDir); - const majorMinorMatches = fullEspIdfVersion.match(/([0-9]+\.[0-9]+).*/); - const espIdfVersion = - majorMinorMatches && majorMinorMatches.length > 0 - ? majorMinorMatches[1] - : "x.x"; - const constrainsFile = join( - idfToolsDir, - `espidf.constraints.v${espIdfVersion}.txt` - ); - const constrainsFileExists = await pathExists(constrainsFile); - let constraintArg = []; - if (constrainsFileExists) { - constraintArg = ["--constraint", constrainsFile]; - } else { - const extensionConstraintsFile = join( - utils.extensionContext.extensionPath, - "espidf.constraints.txt" - ); - const extensionConstraintsFileExists = await pathExists( - extensionConstraintsFile - ); - if (extensionConstraintsFileExists) { - constraintArg = ["--constraint", extensionConstraintsFile]; - } - } - const installDAPyPkgsMsg = `Installing ESP-IDF Debug Adapter python packages in ${virtualEnvPython} ...\n`; - Logger.info(installDAPyPkgsMsg + "\n"); - if (pyTracker) { - pyTracker.Log = installDAPyPkgsMsg; - } - OutputChannel.appendLine(installDAPyPkgsMsg + "\n"); - const args = [ - "-m", - "pip", - "install", - "--upgrade", - ...constraintArg, - "--no-warn-script-location", - "-r", - debugAdapterRequirements, - "--extra-index-url", - "https://dl.espressif.com/pypi", - ]; - await execProcessWithLog(virtualEnvPython, args, pyTracker, opts); -} - export async function installEspMatterPyReqs( - espDir: string, - idfToolsDir: string, + workspaceFolder: Uri, espMatterDir: string, - pythonBinPath: string, pyTracker?: PyReqLog, cancelToken?: CancellationToken ) { const modifiedEnv: { [key: string]: string } = <{ [key: string]: string }>( Object.assign({}, process.env) ); - const opts = { env: modifiedEnv, cwd: idfToolsDir, cancelToken }; - const virtualEnvPython = await getPythonEnvPath( - espDir, - idfToolsDir, - pythonBinPath - ); + const opts = { env: modifiedEnv, cwd: workspaceFolder.fsPath }; + const virtualEnvPython = await getVirtualEnvPythonPath(); const reqDoesNotExists = " doesn't exist. Make sure the path is correct."; const matterRequirements = join(espMatterDir, "requirements.txt"); @@ -299,129 +158,23 @@ export async function execProcessWithLog( } } -export async function getVirtualEnvPythonPath(workspaceFolder: Uri) { - let pythonPath = readParameter( - "idf.pythonInstallPath", - workspaceFolder - ) as string; - let espIdfDir = readParameter("idf.espIdfPath", workspaceFolder) as string; - let idfToolsDir = readParameter("idf.toolsPath", workspaceFolder) as string; - const idfPathExists = await pathExists(espIdfDir); - const idfToolsPathExists = await pathExists(idfToolsDir); - const pythonPathExists = await pathExists(pythonPath); - if (!idfPathExists || !idfToolsPathExists || !pythonPathExists) { - return; - } - const virtualEnvPython = await getPythonEnvPath( - espIdfDir, - idfToolsDir, - pythonPath - ); - return virtualEnvPython; -} - -export async function getPythonPath(workspaceFolder: Uri) { - let sysPythonBinPath = readParameter( - "idf.pythonInstallPath", - workspaceFolder - ) as string; - const doesSysPythonBinPathExist = await pathExists(sysPythonBinPath); - if (!doesSysPythonBinPathExist) { - sysPythonBinPath = await getSystemPython(workspaceFolder); - if (sysPythonBinPath) { - await writeParameter( - "idf.pythonInstallPath", - sysPythonBinPath, - workspaceFolder - ? ConfigurationTarget.WorkspaceFolder - : ConfigurationTarget.Global, - workspaceFolder - ); - } - } - return sysPythonBinPath; -} - -export async function getSystemPythonFromSettings( - pythonBinPath: string, - espIdfPath: string, - espIdfToolsPath: string -) { - const pythonBinPathExists = await pathExists(pythonBinPath); - if (pythonBinPathExists) { - const pythonCode = `import sys; print('{}'.format(sys.base_prefix))`; - const args = ["-c", pythonCode]; - const pythonVersion = ( - await utils.execChildProcess(pythonBinPath, args, __dirname) - ).replace(/(\n|\r|\r\n)/gm, ""); +export async function getVirtualEnvPythonPath() { + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); + if (currentEnvVars["IDF_PYTHON_ENV_PATH"]) { const pyDir = - process.platform === "win32" ? ["python.exe"] : ["bin", "python3"]; - const sysPythonBinPath = join(pythonVersion, ...pyDir); - return sysPythonBinPath; - } - - if (process.platform !== "win32") { - const sysPythonBinPathList = await getUnixPythonList(__dirname); - return sysPythonBinPathList.length ? sysPythonBinPathList[0] : "python3"; - } else { - const idfVersion = await utils.getEspIdfFromCMake(espIdfPath); - const pythonVersionToUse = - idfVersion >= "5.0" - ? ESP.URL.IDF_EMBED_PYTHON.VERSION - : ESP.URL.OLD_IDF_EMBED_PYTHON.VERSION; - const idfPyDestPath = join( - espIdfToolsPath, - "tools", - "idf-python", - pythonVersionToUse, - "python.exe" + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python3"]; + const venvPythonPath = join( + currentEnvVars["IDF_PYTHON_ENV_PATH"], + ...pyDir ); - const idfPyDestExists = await pathExists(idfPyDestPath); - return idfPyDestExists ? idfPyDestPath : ""; + return venvPythonPath; } } -export async function getSystemPython(workspaceFolder: Uri) { - let pythonBinPath = readParameter( - "idf.pythonBinPath", - workspaceFolder - ) as string; - const idfPathDir = readParameter("idf.espIdfPath", workspaceFolder); - const idfToolsDir = readParameter("idf.toolsPath", workspaceFolder) as string; - return await getSystemPythonFromSettings( - pythonBinPath, - idfPathDir, - idfToolsDir - ); -} - -export async function getPythonEnvPath( - espIdfDir: string, - idfToolsDir: string, - pythonBin: string -) { - const pythonCode = `import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))`; - const args = ["-c", pythonCode]; - const pythonVersion = ( - await utils.execChildProcess(pythonBin, args, espIdfDir) - ).replace(/(\n|\r|\r\n)/gm, ""); - const fullEspIdfVersion = await utils.getEspIdfFromCMake(espIdfDir); - const majorMinorMatches = fullEspIdfVersion.match(/([0-9]+\.[0-9]+).*/); - const espIdfVersion = - majorMinorMatches && majorMinorMatches.length > 0 - ? majorMinorMatches[1] - : "x.x"; - const resultVersion = `idf${espIdfVersion}_py${pythonVersion}_env`; - const idfPyEnvPath = join(idfToolsDir, "python_env", resultVersion); - const pyDir = - process.platform === "win32" - ? ["Scripts", "python.exe"] - : ["bin", "python"]; - const fullIdfPyEnvPath = join(idfPyEnvPath, ...pyDir); - const pyEnvPathExists = await pathExists(fullIdfPyEnvPath); - return pyEnvPathExists ? fullIdfPyEnvPath : ""; -} - export async function checkPythonExists(pythonBin: string, workingDir: string) { try { const versionResult = await utils.execChildProcess( diff --git a/src/qemu/qemuManager.ts b/src/qemu/qemuManager.ts index 239c51b1f..5c512a0de 100644 --- a/src/qemu/qemuManager.ts +++ b/src/qemu/qemuManager.ts @@ -32,10 +32,11 @@ import { readParameter } from "../idfConfiguration"; import { Logger } from "../logger/logger"; import { statusBarItems } from "../statusBar"; import { CommandKeys, createCommandDictionary } from "../cmdTreeView/cmdStore"; -import { appendIdfAndToolsToPath, isBinInPath } from "../utils"; +import { isBinInPath } from "../utils"; import { IdfToolsManager } from "../idfToolsManager"; -import { getVirtualEnvPythonPath } from "../pythonManager"; +import { configureEnvVariables } from "../common/prepareEnv"; import { join } from "path"; +import { getVirtualEnvPythonPath } from "../pythonManager"; export enum QemuLaunchMode { Debug, @@ -107,16 +108,14 @@ export class QemuManager extends EventEmitter { } } - public getLaunchArguments(mode: QemuLaunchMode, workspaceFolder: Uri) { + public async getLaunchArguments(mode: QemuLaunchMode, workspaceFolder: Uri) { const buildPath = readParameter("idf.buildPath", workspaceFolder) as string; const extraArgs = readParameter( "idf.qemuExtraArgs", workspaceFolder ) as string[]; - const idfPathDir = readParameter( - "idf.espIdfPath", - workspaceFolder - ) as string; + const modifiedEnv = await configureEnvVariables(workspaceFolder); + const idfPathDir = modifiedEnv["IDF_PATH"]; const idfPy = join(idfPathDir, "tools", "idf.py"); let launchArgs = [idfPy, "-B", buildPath, "qemu"]; @@ -172,7 +171,7 @@ export class QemuManager extends EventEmitter { if (this.isRunning()) { return; } - const modifiedEnv = await appendIdfAndToolsToPath(workspaceFolder); + const modifiedEnv = await configureEnvVariables(workspaceFolder); const qemuExecutableDict = await this.getQemuExecutable( modifiedEnv.IDF_PATH ); @@ -192,7 +191,7 @@ export class QemuManager extends EventEmitter { ); } - const qemuArgs: string[] = this.getLaunchArguments(mode, workspaceFolder); + const qemuArgs: string[] = await this.getLaunchArguments(mode, workspaceFolder); if (typeof qemuArgs === "undefined" || qemuArgs.length < 1) { throw new Error("No QEMU launch arguments found."); } @@ -212,7 +211,7 @@ export class QemuManager extends EventEmitter { } }); } - const pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); + const pythonBinPath = await getVirtualEnvPythonPath(); this.qemuTerminal.sendText(`${pythonBinPath} ${qemuArgs.join(" ")}`); this.qemuTerminal.show(true); this.updateStatusText("❇️ QEMU Server (Running)"); diff --git a/src/rainmaker/download/espRainmakerDownload.ts b/src/rainmaker/download/espRainmakerDownload.ts index 522f58799..fbc7d5410 100644 --- a/src/rainmaker/download/espRainmakerDownload.ts +++ b/src/rainmaker/download/espRainmakerDownload.ts @@ -35,5 +35,5 @@ export class RainmakerCloning extends AbstractCloning { export async function getEspRainmaker(workspace?: Uri) { const gitPath = (readParameter("idf.gitPath", workspace) as string) || "git"; const rainmakerInstaller = new RainmakerCloning(gitPath); - await rainmakerInstaller.getRepository("idf.espRainmakerPath", workspace); + await rainmakerInstaller.getRepository("RMAKER_PATH", workspace); } diff --git a/src/setup/SetupPanel.ts b/src/setup/SetupPanel.ts deleted file mode 100644 index 9ffd3817f..000000000 --- a/src/setup/SetupPanel.ts +++ /dev/null @@ -1,794 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { ISetupInitArgs } from "./setupInit"; -import { - IEspIdfLink, - IEspIdfTool, - SetupMode, - StatusType, -} from "../views/setup/types"; -import { ESP } from "../config"; -import * as idfConf from "../idfConfiguration"; -import { ensureDir } from "fs-extra"; -import path from "path"; -import * as fs from "fs"; -import { - CancellationToken, - ConfigurationTarget, - Disposable, - ExtensionContext, - Progress, - ProgressLocation, - StatusBarItem, - Uri, - ViewColumn, - WebviewPanel, - commands, - l10n, - window, -} from "vscode"; -import { expressInstall } from "./espIdfDownloadStep"; -import { IdfToolsManager } from "../idfToolsManager"; -import { OutputChannel } from "../logger/outputChannel"; -import { Logger } from "../logger/logger"; -import { createPyReqs } from "./pyReqsInstallStep"; -import { downloadIdfTools } from "./toolsDownloadStep"; -import { installIdfGit, installIdfPython } from "./embedGitPy"; -import { getOpenOcdRules } from "./addOpenOcdRules"; -import { - checkSpacesInPath, - getEspIdfFromCMake, - canAccessFile, - compareVersion, -} from "../utils"; -import { useIdfSetupSettings } from "./setupValidation/espIdfSetup"; -import { clearPreviousIdfSetups } from "./existingIdfSetups"; - -export class SetupPanel { - public static currentPanel: SetupPanel | undefined; - - public static createOrShow( - context: ExtensionContext, - setupArgs?: ISetupInitArgs - ) { - const column = window.activeTextEditor - ? window.activeTextEditor.viewColumn - : ViewColumn.One; - if (SetupPanel.currentPanel) { - SetupPanel.currentPanel.panel.reveal(column); - } else { - SetupPanel.currentPanel = new SetupPanel(context, column, setupArgs); - } - } - - public static isCreatedAndHidden(): boolean { - return ( - SetupPanel.currentPanel && SetupPanel.currentPanel.panel.visible === false - ); - } - - public static postMessage(content: any) { - if (SetupPanel.currentPanel) { - SetupPanel.currentPanel.panel.webview.postMessage(content); - } - } - - private static readonly viewType = "setupPanel"; - private readonly panel: WebviewPanel; - private disposables: Disposable[] = []; - - constructor( - private context: ExtensionContext, - column: ViewColumn, - setupArgs: ISetupInitArgs - ) { - const setupPanelTitle = l10n.t("ESP-IDF Setup"); - - this.panel = window.createWebviewPanel( - SetupPanel.viewType, - setupPanelTitle, - column, - { - enableScripts: true, - retainContextWhenHidden: true, - localResourceRoots: [ - Uri.file(path.join(this.context.extensionPath, "dist", "views")), - ], - } - ); - this.panel.iconPath = Uri.file( - path.join(context.extensionPath, "media", "espressif_icon.png") - ); - - const scriptPath = this.panel.webview.asWebviewUri( - Uri.file( - path.join(context.extensionPath, "dist", "views", "setup-bundle.js") - ) - ); - this.panel.webview.html = this.createSetupHtml(scriptPath); - - const containerPath = - process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; - const defaultEspIdfPathContainer = path.join(containerPath, "esp"); - - this.panel.webview.onDidReceiveMessage(async (message) => { - switch (message.command) { - case "checkEspIdfTools": - if (message.espIdf && message.pyPath && message.toolsPath) { - await this.checkRequiredTools( - message.espIdf, - JSON.parse(message.toolsPath), - setupArgs.onReqPkgs - ); - } - break; - case "installEspIdf": - if ( - message.espIdfContainer && - message.selectedEspIdfVersion && - message.toolsPath && - message.saveScope && - typeof message.selectedPyPath !== undefined && - typeof message.manualEspIdfPath !== undefined && - typeof message.mirror !== undefined && - typeof message.setupMode !== undefined - ) { - if ( - message.selectedEspIdfVersion && - message.selectedEspIdfVersion.filename !== "manual" && - message.espIdfContainer === defaultEspIdfPathContainer - ) { - await ensureDir(defaultEspIdfPathContainer); - } - await this.autoInstall( - message.toolsPath, - JSON.parse(message.selectedEspIdfVersion), - message.selectedPyPath, - message.manualEspIdfPath, - message.espIdfContainer, - message.mirror, - message.saveScope, - message.setupMode, - context, - setupArgs.espIdfStatusBar, - setupArgs.workspaceFolder, - setupArgs.onReqPkgs, - message.pypiIndex - ); - } - break; - case "installEspIdfTools": - if ( - message.espIdf && - message.pyPath && - message.toolsPath && - message.saveScope && - typeof message.mirror !== undefined - ) { - await this.installEspIdfTools( - message.espIdf, - message.pyPath, - message.toolsPath, - setupArgs.gitPath, - message.mirror, - message.saveScope, - setupArgs.workspaceFolder, - context, - setupArgs.espIdfStatusBar, - setupArgs.onReqPkgs, - message.pypiIndex - ); - } - break; - case "openEspIdfFolder": - const selectedFolder = await this.openFolder(); - this.panel.webview.postMessage({ - command: "updateEspIdfFolder", - selectedFolder, - }); - break; - case "openEspIdfContainerFolder": - const selectedContainerFolder = await this.openFolder(); - this.panel.webview.postMessage({ - command: "updateEspIdfContainerFolder", - selectedContainerFolder, - }); - break; - case "openEspIdfToolsFolder": - const selectedToolsFolder = await this.openFolder(); - this.panel.webview.postMessage({ - command: "updateEspIdfToolsFolder", - selectedToolsFolder, - }); - break; - case "openPythonPath": - const selectedPyPath = await this.openFile(); - this.panel.webview.postMessage({ - command: "updatePythonPath", - selectedPyPath, - }); - break; - case "requestInitialValues": - const pathSep = path.sep; - this.panel.webview.postMessage({ - command: "initialLoad", - downloadMirror: setupArgs.downloadMirror, - espIdfContainer: defaultEspIdfPathContainer, - espIdf: setupArgs.espIdfPath, - extensionVersion: setupArgs.extensionVersion, - espToolsPath: setupArgs.espToolsPath, - gitVersion: setupArgs.gitVersion, - hasPrerequisites: setupArgs.hasPrerequisites, - idfVersions: setupArgs.espIdfVersionsList, - idfTags: setupArgs.espIdfTagsList, - idfSetups: setupArgs.existingIdfSetups, - pathSep, - platform: process.platform, - pyVersionList: setupArgs.pythonVersions, - saveScope: setupArgs.saveScope, - }); - break; - case "saveCustomSettings": - if ( - message.espIdfPath && - message.toolsPath && - message.pyBinPath && - message.tools && - message.saveScope && - typeof message.mirror !== undefined - ) { - this.panel.webview.postMessage({ - command: "updateEspIdfToolsStatus", - status: StatusType.installed, - }); - await this.installPyReqs( - message.espIdfPath, - message.toolsPath, - message.pyBinPath, - setupArgs.gitPath, - message.saveScope, - context, - setupArgs.workspaceFolder, - setupArgs.espIdfStatusBar, - message.mirror, - message.pypiIndex - ); - } - break; - case "useIdfSetup": - if ( - typeof message.selectedIdfSetup !== undefined && - message.saveScope - ) { - this.panel.webview.postMessage({ - command: "updateIdfGitStatus", - status: StatusType.installed, - }); - this.panel.webview.postMessage({ - command: "updateIdfPythonStatus", - status: StatusType.installed, - }); - this.panel.webview.postMessage({ - command: "updateEspIdfStatus", - status: StatusType.installed, - }); - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: `ESP-IDF is installed in ${ - setupArgs.existingIdfSetups[message.selectedIdfSetup].idfPath - }`, - }); - this.panel.webview.postMessage({ - command: "updateEspIdfToolsStatus", - status: StatusType.installed, - }); - this.panel.webview.postMessage({ - command: "updatePyVEnvStatus", - status: StatusType.started, - }); - this.panel.webview.postMessage({ - command: "goToCustomPage", - installing: true, - page: "/status", - }); - await useIdfSetupSettings( - setupArgs.existingIdfSetups[message.selectedIdfSetup], - message.saveScope, - setupArgs.workspaceFolder, - setupArgs.espIdfStatusBar - ); - this.panel.webview.postMessage({ - command: "setIsInstalled", - isInstalled: true, - }); - await this.getOpenOcdRulesPath(); - } - break; - case "cleanIdfSetups": - await clearPreviousIdfSetups(); - break; - case "newProject": - await commands.executeCommand("espIdf.newProject.start"); - break; - case "importProject": - await commands.executeCommand("espIdf.importProject"); - break; - case "exploreComponents": - await commands.executeCommand("esp.component-manager.ui.show"); - break; - case "canAccessFile": - if (!message.path) { - break; - } - - const pathIdfPy = path.join(message.path, "tools", "idf.py"); - // Only require read and execute permissions - const fileExists = await canAccessFile( - pathIdfPy, - fs.constants.R_OK | fs.constants.X_OK - ); - if (!fileExists) { - this.panel.webview.postMessage({ - command: "canAccessFileResponse", - path: pathIdfPy, - exists: fileExists, - }); - break; - } - - let versionEspIdf: string; - if ( - message.currentVersion && - typeof message.currentVersion === "string" - ) { - versionEspIdf = message.currentVersion; - } else { - versionEspIdf = await getEspIdfFromCMake(message.path); - } - // compareVersion returns a negative value if versionEspIdf is less than "5.0" - const noWhiteSpaceSupport = compareVersion(versionEspIdf, "5.0") < 0; - const hasWhitespace = /\s/.test(message.path); - this.panel.webview.postMessage({ - command: "canAccessFileResponse", - path: pathIdfPy, - exists: fileExists, - noWhiteSpaceSupport, - hasWhitespace, - }); - break; - - default: - break; - } - }); - - this.panel.onDidDispose(() => this.dispose(), null, this.disposables); - } - - setupErrHandler(error: Error) { - const errMsg = - error && error.message ? error.message : "Error during ESP-IDF setup"; - if ( - errMsg.indexOf("ERROR_EXISTING_ESP_IDF") !== -1 || - errMsg.indexOf("IDF_PATH_WITH_SPACES") !== -1 || - errMsg.indexOf("IDF_TOOLS_PATH_WITH_SPACES") !== -1 || - errMsg.indexOf("ERROR_SAME_IDF_PATH_AND__IDF_TOOLS_PATH") !== -1 - ) { - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: error.message, - }); - } else if ( - errMsg.indexOf("ERROR_INVALID_PYTHON") !== -1 || - errMsg.indexOf("ERROR_INVALID_PIP") !== -1 || - errMsg.indexOf("ERROR_INVALID_VENV") !== -1 || - errMsg.indexOf("PYTHON_BIN_PATH_WITH_SPACES") !== -1 - ) { - SetupPanel.postMessage({ - command: "setPyExecErrorStatus", - errorMsg: error.message, - }); - } else { - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: "", - }); - } - OutputChannel.appendLine(errMsg); - Logger.errorNotify(errMsg, error, "SetupPanel error handler"); - OutputChannel.show(); - SetupPanel.postMessage({ - command: "goToCustomPage", - installing: false, - page: "/autoinstall", - }); - } - - private async autoInstall( - toolsPath: string, - selectedIdfVersion: IEspIdfLink, - pyPath: string, - espIdfPath: string, - idfContainerPath: string, - mirror: ESP.IdfMirror, - saveScope: ConfigurationTarget, - setupMode: SetupMode, - context: ExtensionContext, - espIdfStatusBar: StatusBarItem, - workspaceFolderUri: Uri, - onReqPkgs?: string[], - pypiIndex?: string - ) { - const notificationMode = idfConf.readParameter( - "idf.notificationMode" - ) as string; - const progressLocation = - notificationMode === idfConf.NotificationMode.All || - notificationMode === idfConf.NotificationMode.Notifications - ? ProgressLocation.Notification - : ProgressLocation.Window; - return await window.withProgress( - { - location: progressLocation, - title: "ESP-IDF Setup:", - cancellable: true, - }, - async ( - progress: Progress<{ message: string; increment?: number }>, - cancelToken: CancellationToken - ) => { - try { - SetupPanel.postMessage({ - command: "goToCustomPage", - installing: true, - page: "/status", - }); - let idfPythonPath = pyPath, - idfGitPath = "git"; - if (process.platform === "win32") { - let idfVersion = ""; - if (selectedIdfVersion.filename === "manual") { - idfVersion = await getEspIdfFromCMake(espIdfPath); - } else if (selectedIdfVersion.filename === "master") { - idfVersion = "5.1"; - } else { - const matches = selectedIdfVersion.name - .split(" ")[0] - .match(/v(.+)/); - if (matches && matches.length) { - idfVersion = matches[1]; - } else { - idfVersion = "5.0"; - } - } - const embedPaths = await this.installEmbedPyGit( - toolsPath, - idfVersion, - mirror, - progress, - cancelToken - ); - idfGitPath = embedPaths.idfGitPath; - idfPythonPath = embedPaths.idfPythonPath; - } - const idfPathToCheck = - selectedIdfVersion.filename === "manual" - ? espIdfPath - : idfContainerPath; - this.checkSpacesInPaths(toolsPath); - if (idfPathToCheck === toolsPath) { - const idfPathSameIdfToolsPathMsg = `IDF_PATH and IDF_TOOLS_PATH can't be the same. Please use another location. (ERROR_SAME_IDF_PATH_AND__IDF_TOOLS_PATH)`; - throw new Error(idfPathSameIdfToolsPathMsg); - } - await expressInstall( - selectedIdfVersion, - idfPythonPath, - espIdfPath, - idfContainerPath, - toolsPath, - mirror, - saveScope, - setupMode, - context, - espIdfStatusBar, - workspaceFolderUri, - idfGitPath, - progress, - cancelToken, - onReqPkgs, - pypiIndex - ); - } catch (error) { - this.setupErrHandler(error); - } - } - ); - } - - private async checkRequiredTools( - idfPath: string, - toolsInfo: IEspIdfTool[], - onReqPkgs?: string[] - ) { - const toolsManager = await IdfToolsManager.createIdfToolsManager(idfPath); - const pathToVerify = toolsInfo - .reduce((prev, curr, i) => { - return prev + path.delimiter + curr.path; - }, "") - .slice(1); - - const foundVersions = await toolsManager.verifyPackages( - pathToVerify, - onReqPkgs - ); - const updatedToolsInfo = toolsInfo.map((tool) => { - const isToolVersionCorrect = - tool.expected.indexOf(foundVersions[tool.name]) > -1 || - (foundVersions[tool.name] && - foundVersions[tool.name] === "No command version"); - tool.doesToolExist = isToolVersionCorrect; - if (isToolVersionCorrect) { - tool.progress = "100.00%"; - tool.hashResult = true; - } else { - tool.progress = "0.00%"; - tool.hashResult = false; - } - return tool; - }); - this.panel.webview.postMessage({ - command: "setRequiredToolsInfo", - toolsInfo: updatedToolsInfo, - }); - } - - private async installEspIdfTools( - idfPath: string, - pyPath: string, - toolsPath: string, - gitPath: string, - mirror: ESP.IdfMirror, - saveScope: ConfigurationTarget, - workspaceFolderUri: Uri, - context: ExtensionContext, - espIdfStatusBar: StatusBarItem, - onReqPkgs?: string[], - pypiIndex?: string - ) { - const notificationMode = idfConf.readParameter( - "idf.notificationMode" - ) as string; - const progressLocation = - notificationMode === idfConf.NotificationMode.All || - notificationMode === idfConf.NotificationMode.Notifications - ? ProgressLocation.Notification - : ProgressLocation.Window; - return await window.withProgress( - { - location: progressLocation, - title: "ESP-IDF Tools Setup:", - cancellable: true, - }, - async ( - progress: Progress<{ message: string; increment?: number }>, - cancelToken: CancellationToken - ) => { - try { - SetupPanel.postMessage({ - command: "goToCustomPage", - installing: true, - page: "/status", - }); - let idfPythonPath = pyPath, - idfGitPath = gitPath || "/usr/bin/git"; - if (process.platform === "win32") { - const idfVersion = await getEspIdfFromCMake(idfPath); - const embedPaths = await this.installEmbedPyGit( - toolsPath, - idfVersion, - mirror, - progress, - cancelToken - ); - idfGitPath = embedPaths.idfGitPath; - idfPythonPath = embedPaths.idfPythonPath; - } - this.checkSpacesInPaths(toolsPath); - await downloadIdfTools( - idfPath, - toolsPath, - idfPythonPath, - idfGitPath, - mirror, - saveScope, - workspaceFolderUri, - context, - espIdfStatusBar, - progress, - cancelToken, - onReqPkgs, - pypiIndex - ); - } catch (error) { - this.setupErrHandler(error); - } - } - ); - } - - private async installPyReqs( - idfPath: string, - toolsPath: string, - pyPath: string, - gitPath: string, - saveScope: ConfigurationTarget, - context: ExtensionContext, - workspaceFolderUri: Uri, - espIdfStatusBar: StatusBarItem, - mirror: ESP.IdfMirror, - pypiIndex?: string - ) { - const notificationMode = idfConf.readParameter( - "idf.notificationMode" - ) as string; - const progressLocation = - notificationMode === idfConf.NotificationMode.All || - notificationMode === idfConf.NotificationMode.Notifications - ? ProgressLocation.Notification - : ProgressLocation.Window; - return await window.withProgress( - { - location: progressLocation, - title: "ESP-IDF Python Requirements:", - cancellable: true, - }, - async ( - progress: Progress<{ message: string; increment?: number }>, - cancelToken: CancellationToken - ) => { - try { - SetupPanel.postMessage({ - command: "goToCustomPage", - installing: true, - page: "/status", - }); - await createPyReqs( - idfPath, - toolsPath, - pyPath, - gitPath, - saveScope, - context, - progress, - cancelToken, - workspaceFolderUri, - espIdfStatusBar, - pypiIndex - ); - } catch (error) { - this.setupErrHandler(error); - } - } - ); - } - - private async installEmbedPyGit( - toolsPath: string, - idfVersion: string, - mirror: ESP.IdfMirror, - progress: Progress<{ message: string; increment?: number }>, - cancelToken: CancellationToken - ) { - const idfGitPath = await installIdfGit( - toolsPath, - mirror, - progress, - cancelToken - ); - SetupPanel.postMessage({ - command: "updateIdfGitStatus", - status: StatusType.installed, - }); - const idfPythonPath = await installIdfPython( - toolsPath, - idfVersion, - mirror, - progress, - cancelToken - ); - SetupPanel.postMessage({ - command: "updateIdfPythonStatus", - status: StatusType.installed, - }); - await idfConf.writeParameter( - "idf.pythonInstallPath", - idfPythonPath, - ConfigurationTarget.Global - ); - await idfConf.writeParameter( - "idf.gitPath", - idfGitPath, - ConfigurationTarget.Global - ); - return { idfPythonPath, idfGitPath }; - } - - private async getOpenOcdRulesPath() { - try { - await getOpenOcdRules(Uri.file(this.context.extensionPath)); - } catch (error) { - this.setupErrHandler(error); - } - } - - private checkSpacesInPaths(idfToolsPath: string) { - const doesIdfToolsPathHasSpaces = checkSpacesInPath(idfToolsPath); - let pathHasSpaces = ""; - if (doesIdfToolsPathHasSpaces) { - pathHasSpaces = `${idfToolsPath} has spaces. Use another location. (IDF_TOOLS_PATH_WITH_SPACES)`; - } - if (pathHasSpaces) { - OutputChannel.appendLine(pathHasSpaces); - Logger.infoNotify(pathHasSpaces); - throw new Error(pathHasSpaces); - } - } - - private async openFolder() { - const selectedFolder = await window.showOpenDialog({ - canSelectFolders: true, - canSelectFiles: false, - canSelectMany: false, - }); - if (selectedFolder && selectedFolder.length > 0) { - return selectedFolder[0].fsPath; - } else { - window.showInformationMessage("No folder selected"); - } - } - - private async openFile() { - const selectedFolder = await window.showOpenDialog({ - canSelectFolders: false, - canSelectFiles: true, - canSelectMany: false, - }); - if (selectedFolder && selectedFolder.length > 0) { - return selectedFolder[0].fsPath; - } else { - window.showInformationMessage("No folder selected"); - } - } - - private createSetupHtml(scriptPath: Uri): string { - return ` - - - - - ESP-IDF Setup - - -
- - - `; - } - - public dispose() { - SetupPanel.currentPanel = undefined; - this.panel.dispose(); - } -} diff --git a/src/setup/addOpenOcdRules.ts b/src/setup/addOpenOcdRules.ts deleted file mode 100644 index 7539ca4dd..000000000 --- a/src/setup/addOpenOcdRules.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Thursday, 15th July 2021 4:20:36 pm - * Copyright 2021 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { pathExists } from "fs-extra"; -import { dirname, resolve } from "path"; -import { Uri } from "vscode"; -import { appendIdfAndToolsToPath } from "../utils"; -import { SetupPanel } from "./SetupPanel"; - -export async function getOpenOcdRules(workspace: Uri) { - if (process.platform !== "linux") { - return; - } - const modifiedEnv = await appendIdfAndToolsToPath(workspace); - if (!modifiedEnv.OPENOCD_SCRIPTS) { - throw new Error("OPENOCD_SCRIPTS environment variables is not defined"); - } - const openOCDRulesPath = resolve( - dirname(modifiedEnv.OPENOCD_SCRIPTS), - "contrib", - "60-openocd.rules", - ); - const doesRulesPathExists = await pathExists(openOCDRulesPath); - if (!doesRulesPathExists) { - throw new Error(`${openOCDRulesPath} doesn't exists.`); - - } - SetupPanel.postMessage({ - command: "setOpenOcdRulesPath", - openOCDRulesPath, - }); -} diff --git a/src/setup/embedGitPy.ts b/src/setup/embedGitPy.ts deleted file mode 100644 index ccb87f1bf..000000000 --- a/src/setup/embedGitPy.ts +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Monday, 24th May 2021 7:49:15 pm - * Copyright 2021 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { DownloadManager } from "../downloadManager"; -import { CancellationToken, Progress } from "vscode"; -import { PackageProgress } from "../PackageProgress"; -import { InstallManager } from "../installManager"; -import { basename, join } from "path"; -import { - sendIdfGitDownloadDetail, - sendIdfGitDownloadProgress, - sendIdfPythonDownloadDetail, - sendIdfPythonDownloadProgress, -} from "./webviewMsgMethods"; -import { OutputChannel } from "../logger/outputChannel"; -import { pathExists } from "fs-extra"; -import { checkGitExists, spawn } from "../utils"; -import { checkPythonExists } from "../pythonManager"; -import { ESP } from "../config"; -import { Logger } from "../logger/logger"; - -export async function installIdfGit( - idfToolsDir: string, - mirror: ESP.IdfMirror, - progress?: Progress<{ message: string; increment?: number }>, - cancelToken?: CancellationToken -) { - const downloadManager = new DownloadManager(idfToolsDir); - const installManager = new InstallManager(idfToolsDir); - let gitURLToUse = - mirror === ESP.IdfMirror.Github - ? ESP.URL.IDF_EMBED_GIT.GITHUB_EMBED_GIT_URL - : ESP.URL.IDF_EMBED_GIT.IDF_EMBED_GIT_URL; - const gitSize: number = - mirror === ESP.IdfMirror.Github - ? ESP.URL.IDF_EMBED_GIT.GITHUB_EMBED_GIT_SIZE - : ESP.URL.IDF_EMBED_GIT.IDF_EMBED_GIT_SIZE; - const idfGitZipPath = join(idfToolsDir, "dist", basename(gitURLToUse)); - const idfGitDestPath = join( - idfToolsDir, - "tools", - "idf-git", - ESP.URL.IDF_EMBED_GIT.VERSION - ); - const resultGitPath = join(idfGitDestPath, "cmd", "git.exe"); - const pkgProgress = new PackageProgress( - basename(gitURLToUse), - sendIdfGitDownloadProgress, - null, - sendIdfGitDownloadDetail, - null - ); - - const gitPathExists = await pathExists(idfGitDestPath); - if (gitPathExists) { - const gitDirectory = join(idfGitDestPath, "cmd"); - const binVersion = await checkGitExists(gitDirectory, resultGitPath); - if (!binVersion || binVersion === "Not found") { - const msg = `Using existing ${idfGitDestPath}`; - OutputChannel.appendLine(msg); - Logger.info(msg); - return resultGitPath; - } - } - - const msgDownload = `Downloading ${idfGitZipPath}...`; - progress.report({ message: msgDownload }); - OutputChannel.appendLine(msgDownload); - Logger.info(msgDownload); - await downloadManager.downloadWithResume( - gitURLToUse, - join(idfToolsDir, "dist"), - pkgProgress, - cancelToken, - gitSize - ); - - const doesZipfileExist = await pathExists(idfGitZipPath); - if (!doesZipfileExist) { - throw new Error(`${idfGitZipPath} was not downloaded.`); - } - const installingMsg = `Installing ${idfGitDestPath} ...`; - progress.report({ message: installingMsg }); - OutputChannel.appendLine(installingMsg); - Logger.info(installingMsg); - await installManager.installZipFile( - idfGitZipPath, - idfGitDestPath, - cancelToken - ); - const extractedMsg = `Extracted ${idfGitDestPath} ...`; - progress.report({ message: extractedMsg }); - OutputChannel.appendLine(extractedMsg); - Logger.info(extractedMsg); - return resultGitPath; -} - -export async function installIdfPython( - idfToolsDir: string, - idfVersion: string, - mirror: ESP.IdfMirror, - progress?: Progress<{ message: string; increment?: number }>, - cancelToken?: CancellationToken -) { - const downloadManager = new DownloadManager(idfToolsDir); - const installManager = new InstallManager(idfToolsDir); - let pythonURLToUse: string; - let pythonSize: number; - if (idfVersion >= "5.0") { - pythonURLToUse = - mirror === ESP.IdfMirror.Github - ? ESP.URL.IDF_EMBED_PYTHON.GITHUB_EMBED_PYTHON_URL - : ESP.URL.IDF_EMBED_PYTHON.IDF_EMBED_PYTHON_URL; - pythonSize = - mirror === ESP.IdfMirror.Github - ? ESP.URL.IDF_EMBED_PYTHON.GITHUB_EMBED_PYTHON_SIZE - : ESP.URL.IDF_EMBED_PYTHON.IDF_EMBED_PYTHON_SIZE; - } else { - pythonURLToUse = - mirror === ESP.IdfMirror.Github - ? ESP.URL.OLD_IDF_EMBED_PYTHON.GITHUB_EMBED_PYTHON_URL - : ESP.URL.OLD_IDF_EMBED_PYTHON.IDF_EMBED_PYTHON_URL; - pythonSize = - mirror === ESP.IdfMirror.Github - ? ESP.URL.OLD_IDF_EMBED_PYTHON.GITHUB_EMBED_PYTHON_SIZE - : ESP.URL.OLD_IDF_EMBED_PYTHON.IDF_EMBED_PYTHON_SIZE; - } - const idfPyZipPath = join(idfToolsDir, "dist", basename(pythonURLToUse)); - const pkgProgress = new PackageProgress( - basename(pythonURLToUse), - sendIdfPythonDownloadProgress, - null, - sendIdfPythonDownloadDetail, - null - ); - const pythonVersionToUse = - idfVersion >= "5.0" - ? ESP.URL.IDF_EMBED_PYTHON.VERSION - : ESP.URL.OLD_IDF_EMBED_PYTHON.VERSION; - const idfPyDestPath = join( - idfToolsDir, - "tools", - "idf-python", - pythonVersionToUse - ); - const pyPathExists = await pathExists(idfPyDestPath); - if (pyPathExists) { - const binVersion = await checkPythonExists( - join(idfPyDestPath, "python.exe"), - idfPyDestPath - ); - if (binVersion) { - const usingExistingDestMsg = `Using existing ${idfPyDestPath}`; - OutputChannel.appendLine(usingExistingDestMsg); - Logger.info(usingExistingDestMsg); - return join(idfPyDestPath, "python.exe"); - } - } - progress.report({ message: `Downloading ${idfPyZipPath}...` }); - await downloadManager.downloadWithResume( - pythonURLToUse, - join(idfToolsDir, "dist"), - pkgProgress, - cancelToken, - pythonSize - ); - const doesZipfileExist = await pathExists(idfPyZipPath); - if (!doesZipfileExist) { - throw new Error(`${idfPyZipPath} was not downloaded.`); - } - progress.report({ message: `Installing ${idfPyDestPath}...` }); - await installManager.installZipFile(idfPyZipPath, idfPyDestPath, cancelToken); - const extractePyDestMsg = `Extracted ${idfPyDestPath} ...`; - progress.report({ message: extractePyDestMsg }); - OutputChannel.appendLine(extractePyDestMsg); - if (idfVersion >= "5.0") { - await spawn( - join(idfPyDestPath, "python.exe"), - ["-m", "ensurepip", "--upgrade"], - { cwd: idfPyDestPath } - ); - } else { - await spawn( - join(idfPyDestPath, "python.exe"), - ["-m", "pip", "install", "virtualenv"], - { cwd: idfPyDestPath } - ); - } - - return join(idfPyDestPath, "python.exe"); -} diff --git a/src/setup/espIdfDownload.ts b/src/setup/espIdfDownload.ts deleted file mode 100644 index f3d651204..000000000 --- a/src/setup/espIdfDownload.ts +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as utils from "../utils"; -import { Logger } from "../logger/logger"; -import { ESP } from "../config"; -import { OutputChannel } from "../logger/outputChannel"; -import { DownloadManager } from "../downloadManager"; -import { InstallManager } from "../installManager"; -import { PackageProgress } from "../PackageProgress"; -import { IEspIdfLink } from "../views/setup/types"; -import { - sendEspIdfDownloadDetail, - sendEspIdfDownloadProgress, - sendDownloadedZip, - sendExtractedZip, -} from "./webviewMsgMethods"; -import { ensureDir, pathExists } from "fs-extra"; -import { AbstractCloning } from "../common/abstractCloning"; -import { CancellationToken, Disposable, Progress } from "vscode"; -import { dirname, join } from "path"; - -export class EspIdfCloning extends AbstractCloning { - constructor(branchName: string, gitBinPath: string = "git") { - super( - "https://github.com/espressif/esp-idf.git", - "ESP-IDF", - branchName, - gitBinPath, - "https://gitee.com/EspressifSystems/esp-idf.git" - ); - } -} - - - -export async function downloadInstallIdfVersion( - idfVersion: IEspIdfLink, - destPath: string, - mirror: ESP.IdfMirror, - gitPath?: string, - progress?: Progress<{ message: string; increment?: number }>, - cancelToken?: CancellationToken -) { - const downloadedZipPath = join(destPath, idfVersion.filename); - const extractedDirectory = downloadedZipPath.replace(".zip", ""); - const expectedDirectory = join( - destPath, - idfVersion.version.replace("release/", ""), - "esp-idf" - ); - await ensureDir(destPath); - const expectedDirExists = await utils.dirExistPromise(expectedDirectory); - if (expectedDirExists) { - const espExistsMsg = `${expectedDirectory} already exists. Delete it or use another location. (ERROR_EXISTING_ESP_IDF)`; - OutputChannel.appendLine(espExistsMsg); - Logger.infoNotify(espExistsMsg); - throw new Error(espExistsMsg); - } - const downloadManager = new DownloadManager(destPath); - const installManager = new InstallManager(destPath); - const pkgProgress = new PackageProgress( - idfVersion.name, - sendEspIdfDownloadProgress, - null, - sendEspIdfDownloadDetail, - null - ); - pkgProgress.Progress = `0.00%`; - - if ( - idfVersion.version === "master" || - idfVersion.version.startsWith("release") - || idfVersion.version.endsWith("-dev") - ) { - const downloadByCloneMsg = `Downloading ESP-IDF ${idfVersion.version} using git clone...\n`; - OutputChannel.appendLine(downloadByCloneMsg); - Logger.info(downloadByCloneMsg); - if (progress) { - progress.report({ message: downloadByCloneMsg }); - } - const espIdfCloning = new EspIdfCloning(idfVersion.version, gitPath); - let cancelDisposable: Disposable; - if (cancelToken) { - cancelDisposable = cancelToken.onCancellationRequested(() => { - espIdfCloning.cancel(); - }); - } - - await ensureDir(dirname(expectedDirectory)); - await espIdfCloning.downloadByCloning( - dirname(expectedDirectory), - pkgProgress, - progress, - mirror !== ESP.IdfMirror.Espressif, - mirror - ); - if (mirror === ESP.IdfMirror.Espressif) { - await espIdfCloning.updateSubmodules( - expectedDirectory, - pkgProgress, - progress - ); - } - cancelDisposable.dispose(); - } else { - const downloadByHttpMsg = `Downloading ESP-IDF ${idfVersion.name}...`; - OutputChannel.appendLine(downloadByHttpMsg); - Logger.info(downloadByHttpMsg); - if (progress) { - progress.report({ message: downloadByHttpMsg }); - } - const urlToUse = - mirror === ESP.IdfMirror.Github ? idfVersion.url : idfVersion.mirror; - - OutputChannel.appendLine( - `Using mirror ${ - mirror == ESP.IdfMirror.Espressif ? "Espressif" : "Github" - } with URL ${urlToUse}` - ); - await downloadManager.downloadWithResume( - urlToUse, - destPath, - pkgProgress, - cancelToken - ); - const doesZipfileExist = await pathExists(downloadedZipPath); - if (!doesZipfileExist) { - throw new Error(`${downloadedZipPath} was not downloaded.`); - } - const downloadedMsg = `Downloaded ${idfVersion.name}. Extracting...\n`; - OutputChannel.appendLine(downloadedMsg); - Logger.info(downloadedMsg); - sendDownloadedZip(downloadedZipPath); - await installManager.installZipFile( - downloadedZipPath, - destPath, - cancelToken - ); - const extractedMsg = `Extracted ${downloadedZipPath} in ${destPath}.\n`; - OutputChannel.appendLine(extractedMsg); - Logger.info(extractedMsg); - await utils.robustMove(extractedDirectory, expectedDirectory); - sendExtractedZip(expectedDirectory); - - if (gitPath) { - await utils.fixFileModeGitRepository(expectedDirectory, gitPath); - await utils.cleanDirtyGitRepository(expectedDirectory, gitPath); - } - } - return expectedDirectory; -} diff --git a/src/setup/espIdfDownloadStep.ts b/src/setup/espIdfDownloadStep.ts deleted file mode 100644 index f449b7851..000000000 --- a/src/setup/espIdfDownloadStep.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { pathExists } from "fs-extra"; -import * as vscode from "vscode"; -import { ESP } from "../config"; -import { checkPythonExists } from "../pythonManager"; -import { SetupPanel } from "./SetupPanel"; -import * as utils from "../utils"; -import { IEspIdfLink, SetupMode, StatusType } from "../views/setup/types"; -import { downloadInstallIdfVersion } from "./espIdfDownload"; -import { Logger } from "../logger/logger"; -import { downloadIdfTools } from "./toolsDownloadStep"; -import { join } from "path"; -import { IdfToolsManager } from "../idfToolsManager"; - -export async function expressInstall( - selectedIdfVersion: IEspIdfLink, - pyPath: string, - espIdfPath: string, - idfContainerPath: string, - toolsPath: string, - mirror: ESP.IdfMirror, - saveScope: vscode.ConfigurationTarget, - setupMode: SetupMode, - context: vscode.ExtensionContext, - espIdfStatusBar: vscode.StatusBarItem, - workspaceFolderUri: vscode.Uri, - gitPath?: string, - progress?: vscode.Progress<{ message: string; increment?: number }>, - cancelToken?: vscode.CancellationToken, - onReqPkgs?: string[], - pypiIndexUrl?: string -) { - const pyExists = pyPath === "python" ? true : await pathExists(pyPath); - const doesPythonExists = await checkPythonExists(pyPath, __dirname); - if (!(pyExists && doesPythonExists)) { - const containerNotFoundMsg = `${pyPath} is not valid. (ERROR_INVALID_PYTHON)`; - Logger.infoNotify(containerNotFoundMsg); - throw new Error(containerNotFoundMsg); - } - let idfPath: string; - if (selectedIdfVersion.filename === "manual") { - idfPath = espIdfPath; - } else { - idfPath = await downloadInstallIdfVersion( - selectedIdfVersion, - idfContainerPath, - mirror, - gitPath, - progress, - cancelToken - ); - } - const idfVersion = await utils.getEspIdfFromCMake(idfPath); - if (idfVersion === "x.x") { - throw new Error("Invalid ESP-IDF"); - } - SetupPanel.postMessage({ - command: "updateEspIdfFolder", - selectedFolder: idfPath, - }); - SetupPanel.postMessage({ - command: "updateEspIdfStatus", - status: StatusType.installed, - }); - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: `ESP-IDF is installed in ${idfPath}`, - }); - SetupPanel.postMessage({ - command: "updateEspIdfToolsStatus", - status: StatusType.started, - }); - if (setupMode === SetupMode.advanced) { - SetupPanel.postMessage({ - command: "updatePythonPath", - selectedPyPath: pyPath, - }); - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - espIdfPath - ); - const toolsInfo = await idfToolsManager.getRequiredToolsInfo( - join(toolsPath, "tools"), - undefined, - onReqPkgs - ); - SetupPanel.postMessage({ - command: "setRequiredToolsInfo", - toolsInfo, - }); - SetupPanel.postMessage({ - command: "goToCustomPage", - installing: false, - page: "/custom", - }); - return; - } - await downloadIdfTools( - idfPath, - toolsPath, - pyPath, - gitPath, - mirror, - saveScope, - workspaceFolderUri, - context, - espIdfStatusBar, - progress, - cancelToken, - onReqPkgs, - pypiIndexUrl - ); -} diff --git a/src/setup/espIdfJson.ts b/src/setup/espIdfJson.ts deleted file mode 100644 index 03876dd1e..000000000 --- a/src/setup/espIdfJson.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Monday, 31st May 2021 7:18:37 pm - * Copyright 2021 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createHash } from "crypto"; -import { pathExists, readJson, writeJson } from "fs-extra"; -import { join } from "path"; -import { getEspIdfFromCMake, isBinInPath } from "../utils"; - -export interface EspIdfJson { - $schema: string; - $id: string; - _comment: string; - _warning: string; - gitPath: string; - idfToolsPath: string; - idfSelectedId: string; - idfInstalled: { [key: string]: IdfInstalled }; -} - -export interface IdfInstalled { - version: string; - python: string; - path: string; -} - -export function getEspIdfJsonTemplate(toolsPath: string) { - return { - $schema: "http://json-schema.org/schema#", - $id: "http://dl.espressif.com/dl/schemas/esp_idf", - _comment: "Configuration file for ESP-IDF IDEs.", - _warning: - "Use / or \\ when specifying path. Single backslash is not allowed by JSON format.", - gitPath: "", - idfToolsPath: toolsPath, - idfSelectedId: "", - idfInstalled: {}, - } as EspIdfJson; -} - -export function getIdfMd5sum(idfPath: string) { - const md5Value = createHash("md5") - .update(idfPath.replace(/\\/g, "/")) - .digest("hex"); - return `esp-idf-${md5Value}`; -} - -export async function loadEspIdfJson(toolsPath: string) { - const espIdfJsonPath = join(toolsPath, "esp_idf.json"); - const espIdfJsonExists = await pathExists(espIdfJsonPath); - let espIdfJson: EspIdfJson; - try { - if (!espIdfJsonExists) { - throw new Error(`${espIdfJsonPath} doesn't exists.`); - } - espIdfJson = await readJson(espIdfJsonPath); - } catch (error) { - espIdfJson = getEspIdfJsonTemplate(toolsPath); - } - return espIdfJson; -} - -export async function addIdfPath( - idfPath: string, - pythonPath: string, - toolsPath: string, - gitPath: string -) { - const idfVersion = await getEspIdfFromCMake(idfPath); - const newIdfPathObj: IdfInstalled = { - version: idfVersion, - python: pythonPath, - path: idfPath, - }; - const idfId = getIdfMd5sum(idfPath); - const espIdfObj = await loadEspIdfJson(toolsPath); - - espIdfObj["idfInstalled"][idfId] = newIdfPathObj; - espIdfObj["idfSelectedId"] = idfId; - if (!espIdfObj.gitPath) { - if (gitPath === "git") { - gitPath = await isBinInPath(gitPath, process.env); - } - espIdfObj.gitPath = gitPath; - } - const espIdfJsonPath = join(toolsPath, "esp_idf.json"); - await writeJson(espIdfJsonPath, espIdfObj, { spaces: 2 }); -} - -export async function getPropertyFromJson(toolsPath: string, property: string) { - const espIdfObj = await loadEspIdfJson(toolsPath); - return Object.keys(espIdfObj).indexOf(property) !== -1 - ? espIdfObj[property] - : undefined; -} - -export async function getSelectedEspIdfId(toolsPath: string) { - return await getPropertyFromJson(toolsPath, "idfSelectedId"); -} - -export async function getPropertyWithId( - toolsPath: string, - property: string, - id: string -) { - const espIdfObj = await loadEspIdfJson(toolsPath); - return espIdfObj["idfInstalled"][id][property]; -} - -export async function getSelectedEspIdfPath(toolsPath: string) { - const selectedIdfId = await getSelectedEspIdfId(toolsPath); - return await getPropertyWithId(toolsPath, "path", selectedIdfId); -} - -export async function getSelectedIdfInstalled( - toolsPath: string -): Promise { - const espIdfObj = await loadEspIdfJson(toolsPath); - const emptyIDfInstalled = { - version: "", - python: "", - path: "", - }; - return espIdfObj && - espIdfObj.idfSelectedId && - espIdfObj.idfInstalled && - espIdfObj.idfInstalled[espIdfObj.idfSelectedId] - ? espIdfObj.idfInstalled[espIdfObj.idfSelectedId] - : emptyIDfInstalled; -} diff --git a/src/setup/espIdfVersionList.ts b/src/setup/espIdfVersionList.ts deleted file mode 100644 index 3acb1f670..000000000 --- a/src/setup/espIdfVersionList.ts +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { DownloadManager } from "../downloadManager"; -import path from "path"; -import { EOL, tmpdir } from "os"; -import { Logger } from "../logger/logger"; -import { readFile, writeFile, pathExists } from "fs-extra"; -import * as del from "del"; -import { OutputChannel } from "../logger/outputChannel"; -import { IEspIdfLink, IdfMirror } from "../views/setup/types"; -import { ESP } from "../config"; -import axios, { AxiosRequestConfig } from "axios"; - - - -// Cache configuration -const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in milliseconds -const CACHE_FILE = "esp_idf_versions_cache.json"; - -const axiosConfig: AxiosRequestConfig = { - timeout: 30000, // 30 seconds timeout - headers: { - 'User-Agent': ESP.HTTP_USER_AGENT - }, - validateStatus: (status) => status === 200, - maxRedirects: 5 -}; - -interface VersionCache { - content: string; - timestamp: number; - source: string; -} - -function isCacheValid(cache: VersionCache): boolean { - return Date.now() - cache.timestamp < CACHE_DURATION; -} - -function getCacheFilePath(extensionPath: string): string { - return path.join(extensionPath, CACHE_FILE); -} - -async function loadCache(extensionPath: string): Promise { - try { - const cachePath = getCacheFilePath(extensionPath); - if (await pathExists(cachePath)) { - const cacheData = await readFile(cachePath, 'utf8'); - const cache: VersionCache = JSON.parse(cacheData); - if (isCacheValid(cache)) { - OutputChannel.appendLine(`Using cached version list from ${cache.source} (cached ${Math.round((Date.now() - cache.timestamp) / 60000)} minutes ago)`); - return cache; - } - } - } catch (error) { - OutputChannel.appendLine(`Cache load error: ${error.message}`); - } - return null; -} - -async function saveCache(extensionPath: string, content: string, source: string): Promise { - try { - const cache: VersionCache = { - content, - timestamp: Date.now(), - source - }; - const cachePath = getCacheFilePath(extensionPath); - await writeFile(cachePath, JSON.stringify(cache, null, 2)); - OutputChannel.appendLine(`Cached version list from ${source}`); - } catch (error) { - OutputChannel.appendLine(`Cache save error: ${error.message}`); - } -} - -async function checkNetworkConnectivity(): Promise { - try { - await axios.get('https://httpbin.org/get', { timeout: 5000 }); - return true; - } catch (error) { - OutputChannel.appendLine(`Network connectivity check failed: ${error.message}`); - return false; - } -} - -export async function clearVersionCache(extensionPath: string): Promise { - try { - const cachePath = getCacheFilePath(extensionPath); - if (await pathExists(cachePath)) { - await writeFile(cachePath, ''); - OutputChannel.appendLine('Version cache cleared'); - } - } catch (error) { - OutputChannel.appendLine(`Error clearing cache: ${error.message}`); - } -} - -export async function getEspIdfVersions(extensionPath: string) { - const downloadManager = new DownloadManager(extensionPath); - const versionList = await downloadEspIdfVersionList( - downloadManager, - extensionPath - ); - const manualVersion = { - name: "Find ESP-IDF in your system", - filename: "manual", - } as IEspIdfLink; - return [manualVersion, ...versionList]; -} - -export async function downloadEspIdfVersionList( - downloadManager: DownloadManager, - extensionPath: string -) { - // First, try to load from cache - const cachedData = await loadCache(extensionPath); - if (cachedData) { - const versionList = cachedData.content.trim().split("\n"); - return createEspIdfLinkList(versionList); - } - - // Check network connectivity before attempting downloads - const isNetworkAvailable = await checkNetworkConnectivity(); - if (!isNetworkAvailable) { - OutputChannel.appendLine("Network connectivity check failed, using fallback methods"); - } - - // Try primary URL using the enhanced DownloadManager - try { - OutputChannel.appendLine(`Attempting to download ESP-IDF versions from ${ESP.URL.IDF_VERSIONS} using enhanced DownloadManager...`); - - // Create a temporary file path for the version list - const tempDir = tmpdir(); - const tempFilePath = path.join(tempDir, "idf_versions.txt"); - - await downloadManager.downloadWithResume( - ESP.URL.IDF_VERSIONS, - tempDir, - undefined, - undefined, - undefined - ); - - // Read the downloaded content - const fileContent = await readFile(tempFilePath); - const content = fileContent.toString(); - const versionList = content.trim().split("\n"); - - // Save to cache for future use - await saveCache(extensionPath, content, ESP.URL.IDF_VERSIONS); - - // Clean up temporary file - try { - await del(tempFilePath, { force: true }); - } catch (cleanupError) { - // Ignore cleanup errors - } - - OutputChannel.appendLine(`Successfully downloaded ESP-IDF versions from ${ESP.URL.IDF_VERSIONS} using enhanced DownloadManager`); - return createEspIdfLinkList(versionList); - } catch (error) { - const errorMsg = `Failed to download ESP-IDF versions from ${ESP.URL.IDF_VERSIONS}: ${error.message}`; - OutputChannel.appendLine(errorMsg); - Logger.errorNotify(errorMsg, error, "espIdfVersionList downloadEspIdfVersionList"); - } - - // If primary URL fails, try GitHub releases API as fallback - try { - OutputChannel.appendLine("Primary URL failed, trying GitHub releases API as fallback..."); - const githubReleases = await getEspIdfTags("releases"); - if (githubReleases.length > 0) { - OutputChannel.appendLine(`Successfully retrieved ${githubReleases.length} versions from GitHub releases API`); - return githubReleases; - } - } catch (error) { - const errorMsg = `GitHub releases API fallback failed: ${error.message}`; - OutputChannel.appendLine(errorMsg); - Logger.error(errorMsg, error, "espIdfVersionList downloadEspIdfVersionList github fallback"); - } - - // Try local fallback file - try { - const idfVersionListFallBack = path.join( - extensionPath, - "idf_versions.txt" - ); - const fallbackContent = await readFile(idfVersionListFallBack); - const versionList = fallbackContent.toString().trim().split(EOL); - OutputChannel.appendLine("Using local fallback version list file"); - return createEspIdfLinkList(versionList); - } catch (fallbackError) { - const fallBackErrMsg = `Error opening esp-idf fallback version list file. ${fallbackError.message}`; - OutputChannel.appendLine(fallBackErrMsg); - Logger.errorNotify(fallBackErrMsg, fallbackError, "espIdfVersionList downloadEspIdfVersionList fallback"); - - // Return a minimal version list as last resort - OutputChannel.appendLine("All download methods failed, returning minimal version list"); - return createEspIdfLinkList([ - "v5.5-rc1", - "v5.4.2", - "v5.3.3", - "v5.2.5", - "v5.1.6", - "release/v5.5", - "release/v5.4", - "release/v5.3", - "release/v5.2", - "release/v5.1", - "master" - ]); - } -} - -export async function getEspIdfTags(type: 'releases' | 'tags' = 'releases') { - // Define sources based on the requested type - const sources = type === 'releases' - ? [ - { - url: "https://api.github.com/repos/espressif/esp-idf/releases?per_page=100", - name: "GitHub Releases API" - } - ] - : [ - { - url: "https://api.github.com/repos/espressif/esp-idf/tags?per_page=100", - name: "GitHub Tags API" - } - ]; - - for (const source of sources) { - try { - OutputChannel.appendLine(`Attempting to get ESP-IDF ${type} from ${source.name}...`); - const response = await axios.get(source.url, axiosConfig); - - let versionsList: string[] = []; - if (type === 'releases') { - // Handle releases - versionsList = response.data.map((item) => item.tag_name); - } else { - // Handle tags - versionsList = response.data.map((item) => item.name); - } - - OutputChannel.appendLine(`Successfully retrieved ${versionsList.length} ${type} from ${source.name}`); - return createEspIdfLinkList(versionsList); - } catch (error) { - const errorMsg = `Error getting ESP-IDF ${type} from ${source.name}: ${error.message}`; - OutputChannel.appendLine(errorMsg); - Logger.error(errorMsg, error, "espIdfVersionList getEspIdfTags"); - continue; // Try next source - } - } - - // If all sources fail, return empty list - OutputChannel.appendLine(`All ${type} sources failed, returning empty ${type} list`); - return []; -} - -export function createEspIdfLinkList(versionList: string[]) { - const versionZip = - "https://github.com/espressif/esp-idf/releases/download/IDFZIPFileVersion/esp-idf-IDFZIPFileVersion.zip"; - const mirrorZip = `${ESP.URL.IDF_GITHUB_ASSETS}/espressif/esp-idf/releases/download/IDFZIPFileVersion/esp-idf-IDFZIPFileVersion.zip`; - const versionRegex = /\b(IDFZIPFileVersion)\b/g; - const espIdfMasterZip = - "https://github.com/espressif/esp-idf/archive/master.zip"; - const preReleaseRegex = /v.+-rc/g; - const betaRegex = /v.+-beta/g; - const downloadList: IEspIdfLink[] = versionList.map((version) => { - if (version.startsWith("release/")) { - return { - filename: `${version}`, - name: version + " (release branch)", - url: `https://github.com/espressif/esp-idf/archive/refs/heads/${version}.zip`, - mirror: "", - version, - }; - } else if (version.startsWith("v")) { - return { - filename: `esp-idf-${version}.zip`, - name: version + " (release version)", - url: versionZip.replace(versionRegex, version), - mirror: mirrorZip.replace(versionRegex, version), - version, - }; - } else if (preReleaseRegex.test(version)) { - return { - filename: `esp-idf-${version}.zip`, - name: version + " (pre-release version)", - url: versionZip.replace(versionRegex, version), - mirror: mirrorZip.replace(versionRegex, version), - version, - }; - } else if (version === "master") { - return { - filename: `master`, - name: version + " (development branch)", - url: `https://github.com/espressif/esp-idf/archive/refs/heads/${version}.zip`, - mirror: espIdfMasterZip, - version, - }; - } else if (betaRegex.test(version)) { - return { - filename: `esp-idf-${version}.zip`, - name: version + " (beta version)", - url: versionZip.replace(versionRegex, version), - mirror: mirrorZip.replace(versionRegex, version), - version, - }; - } - }); - return downloadList; -} diff --git a/src/setup/existingIdfSetups.ts b/src/setup/existingIdfSetups.ts deleted file mode 100644 index 99b7dcf83..000000000 --- a/src/setup/existingIdfSetups.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Friday, 24th February 2023 10:30:49 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ESP } from "../config"; -import { Logger } from "../logger/logger"; -import { getEspIdfFromCMake } from "../utils"; -import { IdfSetup } from "../views/setup/types"; -import { getIdfMd5sum, loadEspIdfJson } from "./espIdfJson"; -import { checkIdfSetup } from "./setupValidation/espIdfSetup"; - -export async function getPreviousIdfSetups(logToChannel: boolean = true) { - const setupKeys = ESP.GlobalConfiguration.store.getIdfSetupKeys(); - const idfSetups: IdfSetup[] = []; - for (let idfSetupKey of setupKeys) { - let idfSetup = ESP.GlobalConfiguration.store.get( - idfSetupKey, - undefined - ); - if (idfSetup && idfSetup.idfPath) { - try { - idfSetup.isValid = await checkIdfSetup(idfSetup, logToChannel); - idfSetup.version = await getEspIdfFromCMake(idfSetup.idfPath); - idfSetups.push(idfSetup); - } catch (err) { - const msg = err.message - ? err.message - : "Error checkIdfSetup in getPreviousIdfSetups"; - Logger.error(msg, err, "getPreviousIdfSetups"); - ESP.GlobalConfiguration.store.clearIdfSetup(idfSetup.id); - } - } - } - return idfSetups; -} - -export async function clearPreviousIdfSetups() { - const setupKeys = ESP.GlobalConfiguration.store.getIdfSetupKeys(); - for (let idfSetupKey of setupKeys) { - ESP.GlobalConfiguration.store.clear(idfSetupKey); - } - ESP.GlobalConfiguration.store.clear(ESP.GlobalConfiguration.IDF_SETUPS); -} - -export async function createIdfSetup( - idfPath: string, - toolsPath: string, - sysPythonBinPath: string, - gitPath: string -) { - const idfSetupId = getIdfMd5sum(idfPath); - const idfVersion = await getEspIdfFromCMake(idfPath); - const newIdfSetup: IdfSetup = { - id: idfSetupId, - idfPath, - gitPath, - toolsPath, - sysPythonPath: sysPythonBinPath, - version: idfVersion, - isValid: false, - }; - newIdfSetup.isValid = await checkIdfSetup(newIdfSetup); - addIdfSetup(newIdfSetup); - return newIdfSetup; -} - -export function addIdfSetup(newIdfSetup: IdfSetup) { - const setupKeys = ESP.GlobalConfiguration.store.getIdfSetupKeys(); - if (setupKeys.indexOf(newIdfSetup.id) === -1) { - setupKeys.push(newIdfSetup.id); - ESP.GlobalConfiguration.store.updateIdfSetupKeys(setupKeys); - } - ESP.GlobalConfiguration.store.set(newIdfSetup.id, newIdfSetup); -} - -export async function loadIdfSetupsFromEspIdfJson(toolsPath: string) { - const espIdfJson = await loadEspIdfJson(toolsPath); - let idfSetups: IdfSetup[] = []; - if ( - espIdfJson && - espIdfJson.idfInstalled && - Object.keys(espIdfJson.idfInstalled).length - ) { - for (let idfInstalledKey of Object.keys(espIdfJson.idfInstalled)) { - let setupConf: IdfSetup = { - id: idfInstalledKey, - idfPath: espIdfJson.idfInstalled[idfInstalledKey].path, - gitPath: espIdfJson.gitPath, - version: espIdfJson.idfInstalled[idfInstalledKey].version, - python: espIdfJson.idfInstalled[idfInstalledKey].python, - toolsPath: toolsPath, - isValid: false, - } as IdfSetup; - try { - setupConf.isValid = await checkIdfSetup(setupConf, false); - } catch (err) { - const msg = err.message - ? err.message - : "Error checkIdfSetup in loadIdfSetupsFromEspIdfJson"; - Logger.error(msg, err, "loadIdfSetupsFromEspIdfJson"); - setupConf.isValid = false; - } - idfSetups.push(setupConf); - } - } - return idfSetups; -} diff --git a/src/setup/installPyReqs.ts b/src/setup/installPyReqs.ts deleted file mode 100644 index c5b86ed65..000000000 --- a/src/setup/installPyReqs.ts +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { pathExists } from "fs-extra"; -import * as pythonManager from "../pythonManager"; -import { SetupPanel } from "./SetupPanel"; -import { OutputChannel } from "../logger/outputChannel"; -import { PyReqLog } from "../PyReqLog"; -import { CancellationToken, ExtensionContext, Progress } from "vscode"; -import { Logger } from "../logger/logger"; - -export async function installPyReqs( - espIdfPath: string, - workingDir: string, - sysPyBinPath: string, - gitPath: string, - context: ExtensionContext, - progress: Progress<{ message: string; increment?: number }>, - cancelToken?: CancellationToken, - pypiIndexUrl?: string -) { - progress.report({ - message: `Checking Python and pip exists...`, - }); - const pyExists = - sysPyBinPath === "python" ? true : await pathExists(sysPyBinPath); - const doesPythonExists = await pythonManager.checkPythonExists( - sysPyBinPath, - workingDir - ); - if (!(pyExists && doesPythonExists)) { - const msg = "Python have not been found in your environment."; - sendPyReqLog(msg); - OutputChannel.appendLine(msg); - Logger.info(msg); - return; - } - const isNotVirtualEnv = await pythonManager.checkIfNotVirtualEnv( - sysPyBinPath, - workingDir - ); - if (!isNotVirtualEnv) { - const msg = - "Selected python is from a virtual environment. Choose system python"; - sendPyReqLog(msg); - OutputChannel.appendLine(msg); - Logger.info(msg); - return; - } - const logTracker = new PyReqLog(sendPyReqLog); - progress.report({ - message: `Installing python virtualenv and ESP-IDF python requirements...`, - }); - const virtualEnvPyBin = await pythonManager.installPythonEnvFromIdfTools( - espIdfPath, - workingDir, - logTracker, - sysPyBinPath, - gitPath, - context, - cancelToken, - pypiIndexUrl - ); - if (virtualEnvPyBin) { - if (logTracker.Log.indexOf("Exception") < 0) { - const msg = "Python requirements has been installed"; - OutputChannel.appendLine(msg); - Logger.info(msg); - return virtualEnvPyBin; - } - } - const msg = "Python requirements has not been installed"; - OutputChannel.appendLine(msg); - Logger.info(msg); - return; -} - -export function sendPyReqLog(log: string) { - SetupPanel.postMessage({ - command: "updatePyReqsLog", - pyReqsLog: log, - }); -} - -export async function getPythonList(workingDir: string) { - const pyVersionList = await pythonManager.getPythonBinList(workingDir); - pyVersionList.push("Provide python executable path"); - return pyVersionList; -} diff --git a/src/setup/pyReqsInstallStep.ts b/src/setup/pyReqsInstallStep.ts deleted file mode 100644 index 4ad1119b5..000000000 --- a/src/setup/pyReqsInstallStep.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as vscode from "vscode"; -import { StatusType } from "../views/setup/types"; -import { installPyReqs } from "./installPyReqs"; -import { SetupPanel } from "./SetupPanel"; -import { saveSettings } from "./setupInit"; -import { getOpenOcdRules } from "./addOpenOcdRules"; -import { addIdfPath } from "./espIdfJson"; - -export async function createPyReqs( - idfPath: string, - toolsPath: string, - pyPath: string, - gitPath: string, - saveScope: vscode.ConfigurationTarget, - context: vscode.ExtensionContext, - progress: vscode.Progress<{ message: string; increment?: number }>, - cancelToken: vscode.CancellationToken, - workspaceFolderUri: vscode.Uri, - espIdfStatusBar: vscode.StatusBarItem, - pypiIndexUrl?: string -) { - SetupPanel.postMessage({ - command: "updatePyVEnvStatus", - status: StatusType.started, - }); - const virtualEnvPath = await installPyReqs( - idfPath, - toolsPath, - pyPath, - gitPath, - context, - progress, - cancelToken, - pypiIndexUrl - ); - await saveSettings( - idfPath, - toolsPath, - gitPath, - pyPath, - saveScope, - workspaceFolderUri, - espIdfStatusBar - ); - await addIdfPath(idfPath, virtualEnvPath, toolsPath, gitPath); - SetupPanel.postMessage({ - command: "updatePyVEnvStatus", - status: StatusType.installed, - }); - SetupPanel.postMessage({ - command: "setIsInstalled", - isInstalled: true, - }); - SetupPanel.postMessage({ - command: "setIsIdfInstalling", - installing: false, - }); - await getOpenOcdRules(vscode.Uri.file(idfPath)); -} diff --git a/src/setup/setupInit.ts b/src/setup/setupInit.ts deleted file mode 100644 index 3887d7d1e..000000000 --- a/src/setup/setupInit.ts +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { ConfigurationTarget, Progress, StatusBarItem, Uri, env } from "vscode"; -import { IdfToolsManager } from "../idfToolsManager"; -import * as utils from "../utils"; -import { getEspIdfTags, getEspIdfVersions } from "./espIdfVersionList"; -import { IdfMirror, IdfSetup, IEspIdfLink } from "../views/setup/types"; -import { getPythonList } from "./installPyReqs"; -import { pathExists } from "fs-extra"; -import path from "path"; -import { Logger } from "../logger/logger"; -import * as idfConf from "../idfConfiguration"; -import { getPropertyFromJson, getSelectedIdfInstalled } from "./espIdfJson"; -import { - createIdfSetup, - getPreviousIdfSetups, - loadIdfSetupsFromEspIdfJson, -} from "./existingIdfSetups"; -import { checkPyVenv } from "./setupValidation/pythonEnv"; -import { packageJson } from "../utils"; -import { getPythonPath, getVirtualEnvPythonPath } from "../pythonManager"; -import { CommandKeys, createCommandDictionary } from "../cmdTreeView/cmdStore"; - -export interface ISetupInitArgs { - downloadMirror: IdfMirror; - espIdfPath: string; - espToolsPath: string; - existingIdfSetups: IdfSetup[]; - extensionVersion: string; - espIdfVersionsList: IEspIdfLink[]; - espIdfTagsList: IEspIdfLink[]; - gitPath: string; - gitVersion: string; - hasPrerequisites: boolean; - onReqPkgs?: string[]; - pythonVersions: string[]; - saveScope: number; - workspaceFolder: Uri; - espIdfStatusBar: StatusBarItem; -} - -export interface IPreviousInstallResult { - espIdfPath: string; - toolsPath: string; - gitPath: string; - gitVersion: string; - existingIdfSetups: IdfSetup[]; -} - -export async function checkPreviousInstall( - workspaceFolder: Uri -): Promise { - const containerPath = - process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; - - const confEspIdfPath = idfConf.readParameter( - "idf.espIdfPath", - workspaceFolder - ) as string; - const confToolsPath = idfConf.readParameter( - "idf.toolsPath", - workspaceFolder - ) as string; - - const toolsPath = - confToolsPath || - process.env.IDF_TOOLS_PATH || - path.join(containerPath, ".espressif"); - let espIdfPath = - confEspIdfPath || - process.env.IDF_PATH || - path.join(containerPath, "esp", "esp-idf"); - - let idfPathExists = await pathExists(espIdfPath); - if (!idfPathExists && process.platform === "win32") { - espIdfPath = path.join(process.env.USERPROFILE, "Desktop", "esp-idf"); - } - - const espIdfJsonPath = path.join(toolsPath, "esp_idf.json"); - const espIdfJsonExists = await pathExists(espIdfJsonPath); - let gitPath = - idfConf.readParameter("idf.gitPath", workspaceFolder) || "/usr/bin/git"; - let existingIdfSetups: IdfSetup[] = []; - if (espIdfJsonExists) { - const idfInstalled = await getSelectedIdfInstalled(toolsPath); - if (idfInstalled && idfInstalled.path) { - espIdfPath = idfInstalled.path; - } - const gitPathFromJson = (await getPropertyFromJson( - toolsPath, - "gitPath" - )) as string; - const gitPathExists = await pathExists(gitPathFromJson); - if (gitPathExists) { - gitPath = gitPathFromJson; - } - existingIdfSetups = await loadIdfSetupsFromEspIdfJson(toolsPath); - if (process.env.IDF_TOOLS_PATH && toolsPath !== process.env.IDF_TOOLS_PATH) { - const systemIdfSetups = await loadIdfSetupsFromEspIdfJson(process.env.IDF_TOOLS_PATH); - existingIdfSetups = [...existingIdfSetups, ...systemIdfSetups]; - } - } - - const gitVersion = await utils.checkGitExists(containerPath, gitPath); - - return { - espIdfPath, - toolsPath, - gitPath, - gitVersion, - existingIdfSetups, - }; -} - -export async function getSetupInitialValues( - extensionPath: string, - progress: Progress<{ message: string; increment: number }>, - workspaceFolder: Uri -) { - progress.report({ increment: 20, message: "Getting ESP-IDF versions..." }); - const espIdfVersionsList = await getEspIdfVersions(extensionPath); - progress.report({ increment: 10, message: "Getting ESP-IDF Tags" }); - const espIdfTagsList = await getEspIdfTags("tags"); - progress.report({ increment: 10, message: "Getting Python versions..." }); - const pythonVersions = await getPythonList(extensionPath); - const idfSetups = await getPreviousIdfSetups(false); - const extensionVersion = packageJson.version as string; - const saveScope = idfConf.readParameter("idf.saveScope") as number; - const initialDownloadMirror = - env.language.toLowerCase().indexOf("zh-cn") !== -1 || - env.language.toLowerCase().indexOf("zh-tw") !== -1 - ? IdfMirror.Espressif - : IdfMirror.Github; - const setupInitArgs = { - downloadMirror: initialDownloadMirror, - espIdfVersionsList, - espIdfTagsList, - extensionVersion, - existingIdfSetups: idfSetups, - pythonVersions, - saveScope, - workspaceFolder, - onReqPkgs: ["esp-clang"], - } as ISetupInitArgs; - - try { - progress.report({ - increment: 10, - message: "Checking for previous install...", - }); - - // Get initial paths - const prevInstall = await checkPreviousInstall(workspaceFolder); - if (process.platform !== "win32") { - setupInitArgs.hasPrerequisites = - prevInstall.gitVersion !== "Not found" && - pythonVersions && - pythonVersions.length > 0; - - const canAccessCMake = await utils.isBinInPath( - "cmake", - process.env - ); - - if (canAccessCMake === "") { - setupInitArgs.onReqPkgs = setupInitArgs.onReqPkgs - ? [...setupInitArgs.onReqPkgs, "cmake"] - : ["cmake"]; - } - - const canAccessNinja = await utils.isBinInPath( - "ninja", - process.env - ); - - if (canAccessNinja === "") { - setupInitArgs.onReqPkgs = setupInitArgs.onReqPkgs - ? [...setupInitArgs.onReqPkgs, "ninja"] - : ["ninja"]; - } - } else { - setupInitArgs.hasPrerequisites = true; - } - progress.report({ increment: 20, message: "Preparing setup view..." }); - if (prevInstall) { - setupInitArgs.espIdfPath = prevInstall.espIdfPath; - setupInitArgs.espToolsPath = prevInstall.toolsPath; - setupInitArgs.gitPath = prevInstall.gitPath; - setupInitArgs.gitVersion = prevInstall.gitVersion; - if (prevInstall.existingIdfSetups) { - for (let espIdfJsonSetup of prevInstall.existingIdfSetups) { - const alreadyInExtensionSetup = idfSetups.find((s) => { - return s.idfPath === espIdfJsonSetup.idfPath; - }); - if (typeof alreadyInExtensionSetup === "undefined") { - setupInitArgs.existingIdfSetups.push(espIdfJsonSetup); - } - } - } - } - } catch (error) { - Logger.error(error.message, error, "setupInit getSetupInitialValues"); - } - return setupInitArgs; -} - -export async function isCurrentInstallValid(workspaceFolder: Uri) { - const containerPath = - process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME; - const confToolsPath = idfConf.readParameter( - "idf.toolsPath", - workspaceFolder - ) as string; - const toolsPath = - confToolsPath || - process.env.IDF_TOOLS_PATH || - path.join(containerPath, ".espressif"); - - let espIdfPath = idfConf.readParameter("idf.espIdfPath", workspaceFolder); - let idfPathVersion = await utils.getEspIdfFromCMake(espIdfPath); - if (idfPathVersion === "x.x" && process.platform === "win32") { - espIdfPath = path.join(process.env.USERPROFILE, "Desktop", "esp-idf"); - idfPathVersion = await utils.getEspIdfFromCMake(espIdfPath); - } - if (idfPathVersion === "x.x") { - return false; - } - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - espIdfPath - ); - let extraReqPaths = []; - if (process.platform !== "win32") { - const canAccessCMake = await utils.isBinInPath( - "cmake", - process.env - ); - if (!canAccessCMake) { - extraReqPaths.push("cmake"); - } - const canAccessNinja = await utils.isBinInPath( - "ninja", - process.env - ); - if (!canAccessNinja) { - extraReqPaths.push("ninja"); - } - } - const extraPaths = await idfToolsManager.exportPathsInString( - path.join(toolsPath, "tools"), - extraReqPaths - ); - const toolsInfo = await idfToolsManager.getRequiredToolsInfo( - path.join(toolsPath, "tools"), - extraPaths, - extraReqPaths, - false - ); - const failedToolsResult = toolsInfo.filter( - (tInfo) => - tInfo.actual.indexOf("No match") !== -1 && - tInfo.actual.indexOf("Error") !== -1 - ); - - if (failedToolsResult.length !== 0) { - return false; - } - // FIX use system Python path as setting instead venv - // REMOVE this line after next release - const sysPythonBinPath = await getPythonPath(workspaceFolder); - - let pythonBinPath: string = ""; - if (sysPythonBinPath) { - pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); - } - if (!pythonBinPath) { - pythonBinPath = idfConf.readParameter( - "idf.pythonBinPath", - workspaceFolder - ) as string; - } - const isPyEnvValid = await checkPyVenv(pythonBinPath, espIdfPath); - return isPyEnvValid; -} - -export async function saveSettings( - espIdfPath: string, - toolsPath: string, - gitPath: string, - sysPythonBinPath: string, - saveScope: ConfigurationTarget, - workspaceFolderUri: Uri, - espIdfStatusBar: StatusBarItem, - saveGlobalState: boolean = true -) { - const confTarget = - saveScope || - (idfConf.readParameter("idf.saveScope") as ConfigurationTarget); - let workspaceFolder: Uri; - if (confTarget === ConfigurationTarget.WorkspaceFolder) { - workspaceFolder = workspaceFolderUri; - } - await idfConf.writeParameter( - "idf.espIdfPath", - espIdfPath, - confTarget, - workspaceFolder - ); - await idfConf.writeParameter( - "idf.toolsPath", - toolsPath, - confTarget, - workspaceFolder - ); - await idfConf.writeParameter( - "idf.gitPath", - gitPath, - ConfigurationTarget.Global - ); - await idfConf.writeParameter( - "idf.pythonInstallPath", - sysPythonBinPath, - confTarget, - workspaceFolder - ); - const idfPathVersion = await utils.getEspIdfFromCMake(espIdfPath); - if (saveGlobalState) { - await createIdfSetup(espIdfPath, toolsPath, sysPythonBinPath, gitPath); - } - if (espIdfStatusBar) { - const commandDictionary = createCommandDictionary(); - espIdfStatusBar.text = - `$(${ - commandDictionary[CommandKeys.SelectCurrentIdfVersion].iconId - }) ESP-IDF v` + idfPathVersion; - } - Logger.infoNotify("ESP-IDF has been configured"); -} diff --git a/src/setup/setupValidation/espIdfSetup.ts b/src/setup/setupValidation/espIdfSetup.ts deleted file mode 100644 index ee888c4ce..000000000 --- a/src/setup/setupValidation/espIdfSetup.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Monday, 27th February 2023 3:00:15 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { join } from "path"; -import { IdfSetup } from "../../views/setup/types"; -import { IdfToolsManager } from "../../idfToolsManager"; -import { saveSettings } from "../setupInit"; -import { pathExists } from "fs-extra"; -import { Logger } from "../../logger/logger"; -import { checkPyVenv } from "./pythonEnv"; -import { ConfigurationTarget, StatusBarItem, Uri } from "vscode"; -import { - getPythonEnvPath, - getSystemPythonFromSettings, -} from "../../pythonManager"; - -export async function useIdfSetupSettings( - setupConf: IdfSetup, - saveScope: ConfigurationTarget, - workspaceFolderUri: Uri, - espIdfStatusBar: StatusBarItem -) { - let sysPythonBinPath = ""; - if (setupConf.python) { - sysPythonBinPath = await getSystemPythonFromSettings( - setupConf.python, - setupConf.idfPath, - setupConf.toolsPath - ); - } else { - sysPythonBinPath = setupConf.sysPythonPath; - } - await saveSettings( - setupConf.idfPath, - setupConf.toolsPath, - setupConf.gitPath, - sysPythonBinPath, - saveScope, - workspaceFolderUri, - espIdfStatusBar, - false - ); -} - -export async function checkIdfSetup( - setupConf: IdfSetup, - logToChannel: boolean = true -) { - try { - if (!setupConf.idfPath) { - return false; - } - const doesIdfPathExists = await pathExists(setupConf.idfPath); - if (!doesIdfPathExists) { - return false; - } - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - setupConf.idfPath - ); - const exportedToolsPaths = await idfToolsManager.exportPathsInString( - join(setupConf.toolsPath, "tools"), - ["cmake", "ninja"] - ); - const toolsInfo = await idfToolsManager.getRequiredToolsInfo( - join(setupConf.toolsPath, "tools"), - exportedToolsPaths, - ["cmake", "ninja"], - logToChannel - ); - - const failedToolsResult = toolsInfo.filter( - (tInfo) => - !tInfo.doesToolExist && ["cmake", "ninja"].indexOf(tInfo.name) === -1 - ); - - if (failedToolsResult.length) { - return false; - } - let virtualEnvPython = ""; - if (setupConf.python) { - virtualEnvPython = setupConf.python; - } else { - if (!setupConf.sysPythonPath) { - setupConf.sysPythonPath = await getSystemPythonFromSettings( - "", - setupConf.idfPath, - setupConf.toolsPath - ); - } - virtualEnvPython = await getPythonEnvPath( - setupConf.idfPath, - setupConf.toolsPath, - setupConf.sysPythonPath - ); - } - - const pyEnvReqs = await checkPyVenv(virtualEnvPython, setupConf.idfPath); - return pyEnvReqs; - } catch (error) { - const msg = - error && error.message - ? error.message - : `Error checking Idf Setup ${setupConf.idfPath}`; - Logger.error(msg, error, "espIdfSetup checkIdfSetup"); - return false; - } -} diff --git a/src/setup/setupValidation/pythonEnv.ts b/src/setup/setupValidation/pythonEnv.ts deleted file mode 100644 index 3c94cd13c..000000000 --- a/src/setup/setupValidation/pythonEnv.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Monday, 27th February 2023 7:13:29 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { pathExists } from "fs-extra"; -import { join } from "path"; -import { startPythonReqsProcess } from "../../utils"; - -export async function checkPyVenv(pyVenvPath: string, espIdfPath: string) { - const pyExists = await pathExists(pyVenvPath); - if (!pyExists) { - return false; - } - let requirements: string; - requirements = join( - espIdfPath, - "tools", - "requirements", - "requirements.core.txt" - ); - const coreRequirementsExists = await pathExists(requirements); - if (!coreRequirementsExists) { - requirements = join(espIdfPath, "requirements.txt"); - const requirementsExists = await pathExists(requirements); - if (!requirementsExists) { - return false; - } - } - const reqsResults = await startPythonReqsProcess( - pyVenvPath, - espIdfPath, - requirements - ); - if (reqsResults.indexOf("are not satisfied") > -1) { - return false; - } - return true; -} diff --git a/src/setup/toolInstall.ts b/src/setup/toolInstall.ts deleted file mode 100644 index 27be8f877..000000000 --- a/src/setup/toolInstall.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import { delimiter } from "path"; -import { ESP } from "../config"; -import { Logger } from "../logger/logger"; -import { OutputChannel } from "../logger/outputChannel"; -import { DownloadManager } from "../downloadManager"; -import { InstallManager } from "../installManager"; -import { IdfToolsManager } from "../idfToolsManager"; -import { PackageProgress } from "../PackageProgress"; -import { - sendPkgDownloadPercentage, - sendPkgChecksumResult, - sendPkgDownloadDetail, - sendPkgDownloadFailed, -} from "./webviewMsgMethods"; -import { CancellationToken, Progress } from "vscode"; - -export async function downloadEspIdfTools( - installDir: string, - idfToolsManager: IdfToolsManager, - mirror: ESP.IdfMirror, - progress: Progress<{ message: string; increment?: number }>, - pythonBinPath: string, - cancelToken?: CancellationToken, - onReqPkgs?: string[] -) { - const manyPathsInInstallDir = installDir.split(delimiter); - if (manyPathsInInstallDir.length > 1) { - Logger.infoNotify("Introduce a single path"); - return; - } - const downloadManager = new DownloadManager(installDir); - const installManager = new InstallManager(installDir); - - const packages = await idfToolsManager.getPackageList(onReqPkgs); - const pkgProgress = packages.map((p) => { - return new PackageProgress( - p.name, - sendPkgDownloadPercentage, - sendPkgChecksumResult, - sendPkgDownloadDetail, - sendPkgDownloadFailed - ); - }); - OutputChannel.appendLine(""); - Logger.info(""); - await downloadManager.downloadPackages( - idfToolsManager, - progress, - mirror, - pkgProgress, - cancelToken, - onReqPkgs - ); - OutputChannel.appendLine(""); - Logger.info(""); - await installManager.installPackages( - idfToolsManager, - progress, - pythonBinPath, - cancelToken, - onReqPkgs - ); - OutputChannel.appendLine(""); - Logger.info(""); -} diff --git a/src/setup/toolsDownloadStep.ts b/src/setup/toolsDownloadStep.ts deleted file mode 100644 index aa3b933c3..000000000 --- a/src/setup/toolsDownloadStep.ts +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import * as path from "path"; -import * as vscode from "vscode"; -import { ESP } from "../config"; -import { IdfToolsManager } from "../idfToolsManager"; -import { SetupPanel } from "./SetupPanel"; -import { downloadEspIdfTools } from "./toolInstall"; -import { StatusType } from "../views/setup/types"; -import { createPyReqs } from "./pyReqsInstallStep"; - -export async function downloadIdfTools( - idfPath: string, - toolsPath: string, - pyPath: string, - gitPath: string, - mirror: ESP.IdfMirror, - saveScope: vscode.ConfigurationTarget, - workspaceFolderUri: vscode.Uri, - context: vscode.ExtensionContext, - espIdfStatusBar: vscode.StatusBarItem, - progress?: vscode.Progress<{ message: string; increment?: number }>, - cancelToken?: vscode.CancellationToken, - onReqPkgs?: string[], - pypiIndexUrl?: string -) { - const idfToolsManager = await IdfToolsManager.createIdfToolsManager(idfPath); - const requiredTools = await idfToolsManager.getRequiredToolsInfo( - toolsPath, - undefined, - onReqPkgs - ); - SetupPanel.postMessage({ - command: "setRequiredToolsInfo", - toolsInfo: requiredTools, - }); - await downloadEspIdfTools( - toolsPath, - idfToolsManager, - mirror, - progress, - pyPath, - cancelToken, - onReqPkgs - ); - SetupPanel.postMessage({ - command: "updateEspIdfToolsStatus", - status: StatusType.installed, - }); - await createPyReqs( - idfPath, - toolsPath, - pyPath, - gitPath, - saveScope, - context, - progress, - cancelToken, - workspaceFolderUri, - espIdfStatusBar, - pypiIndexUrl - ); -} diff --git a/src/setup/webviewMsgMethods.ts b/src/setup/webviewMsgMethods.ts deleted file mode 100644 index 406ad85d7..000000000 --- a/src/setup/webviewMsgMethods.ts +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 Espressif Systems (Shanghai) CO LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { SetupPanel } from "./SetupPanel"; - -export function sendEspIdfDownloadProgress( - id: string, - updatedPercentage: string -) { - SetupPanel.postMessage({ - command: "updateIdfDownloadStatusPercentage", - id, - percentage: updatedPercentage, - }); -} - -export function sendEspIdfDownloadDetail(id: string, updatedDetail: string) { - SetupPanel.postMessage({ - command: "updateIdfDownloadStatusDetail", - detail: updatedDetail, - id, - }); -} - -export function sendDownloadedZip(downloadedPath: string) { - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: `ESP-IDF is downloaded in ${downloadedPath}`, - }); -} - -export function sendExtractedZip(extractedPath: string) { - SetupPanel.postMessage({ - command: "setEspIdfErrorStatus", - errorMsg: `ESP-IDF is extracted in ${extractedPath}`, - }); -} - -export function sendPkgDownloadPercentage( - pkgName: string, - updatedPercentage: string -) { - SetupPanel.postMessage({ - command: "updatePkgDownloadPercentage", - id: pkgName, - percentage: updatedPercentage, - }); -} - -export function sendPkgChecksumResult( - pkgName: string, - updatedChecksumResult: boolean -) { - SetupPanel.postMessage({ - command: "updatePkgChecksumResult", - id: pkgName, - hashResult: updatedChecksumResult, - }); -} - -export function sendPkgDownloadDetail(pkgName: string, updatedDetail: string) { - SetupPanel.postMessage({ - command: "updatePkgDownloadDetail", - id: pkgName, - progressDetail: updatedDetail, - }); -} - -export function sendPkgDownloadFailed(pkgName: string, failedFlag: boolean) { - SetupPanel.postMessage({ - command: "updatePkgDownloadFailed", - id: pkgName, - hasFailed: failedFlag, - }); -} - -export function sendIdfGitDownloadProgress( - id: string, - updatedPercentage: string -) { - SetupPanel.postMessage({ - command: "updateIdfGitDownloadPercentage", - id, - percentage: updatedPercentage, - }); -} - -export function sendIdfGitDownloadDetail(id: string, updatedDetail: string) { - SetupPanel.postMessage({ - command: "updateIdfGitDownloadDetail", - detail: updatedDetail, - id, - }); -} - -export function sendIdfPythonDownloadProgress( - id: string, - updatedPercentage: string -) { - SetupPanel.postMessage({ - command: "updateIdfPythonDownloadPercentage", - id, - percentage: updatedPercentage, - }); -} - -export function sendIdfPythonDownloadDetail(id: string, updatedDetail: string) { - SetupPanel.postMessage({ - command: "updateIdfPythonDownloadDetail", - detail: updatedDetail, - id, - }); -} \ No newline at end of file diff --git a/src/statusBar/index.ts b/src/statusBar/index.ts index 3844b928d..cc5e0cdcd 100644 --- a/src/statusBar/index.ts +++ b/src/statusBar/index.ts @@ -28,13 +28,13 @@ import { l10n, ThemeColor, } from "vscode"; -import { getCurrentIdfSetup } from "../versionSwitcher"; import { readParameter } from "../idfConfiguration"; import { ESP } from "../config"; import { CommandItem } from "../cmdTreeView/cmdTreeDataProvider"; import { CommandKeys, createCommandDictionary } from "../cmdTreeView/cmdStore"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; import { pathExists } from "fs-extra"; +import { getEspIdfFromCMake } from "../utils"; export const statusBarItems: { [key: string]: StatusBarItem } = {}; @@ -74,8 +74,9 @@ export async function createCmdsStatusBarItems(workspaceFolder: Uri) { ESP.ProjectConfiguration.PROJECT_CONFIGURATION_FILENAME ); let projectConfExists = await pathExists(projectConfPath); - - let currentIdfVersion = await getCurrentIdfSetup(workspaceFolder, false); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); statusBarItems["workspace"] = createStatusBarItem( `$(${commandDictionary[CommandKeys.pickWorkspace].iconId})`, @@ -85,10 +86,11 @@ export async function createCmdsStatusBarItems(workspaceFolder: Uri) { commandDictionary[CommandKeys.pickWorkspace].checkboxState ); + const idfVersion = await getEspIdfFromCMake(currentEnvVars["IDF_PATH"]); statusBarItems["currentIdfVersion"] = createStatusBarItem( `$(${ commandDictionary[CommandKeys.SelectCurrentIdfVersion].iconId - }) ESP-IDF v${currentIdfVersion.version}`, + }) ESP-IDF v${idfVersion}`, commandDictionary[CommandKeys.SelectCurrentIdfVersion].tooltip, CommandKeys.SelectCurrentIdfVersion, 103, diff --git a/src/support/checkExtensionRequirements.ts b/src/support/checkExtensionRequirements.ts deleted file mode 100644 index 536d5f6d6..000000000 --- a/src/support/checkExtensionRequirements.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Wednesday, 30th December 2020 4:47:28 pm - * Copyright 2020 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { join } from "path"; -import * as vscode from "vscode"; -import { reportObj } from "./types"; -import { checkRequirements } from "./checkEspIdfRequirements"; - -export async function checkDebugAdapterRequirements( - reportedResult: reportObj, - context: vscode.ExtensionContext -) { - try { - const requirementsPath = join( - context.extensionPath, - "esp_debug_adapter", - "requirements.txt" - ); - const result = await checkRequirements( - context, - reportedResult, - requirementsPath - ); - reportedResult.debugAdapterRequirements.output = result; - reportedResult.debugAdapterRequirements.result = result; - } catch (error) { - reportedResult.debugAdapterRequirements.result = "Error"; - reportedResult.latestError = error; - } -} diff --git a/src/support/checkIdfSetups.ts b/src/support/checkIdfSetups.ts new file mode 100644 index 000000000..f7399a055 --- /dev/null +++ b/src/support/checkIdfSetups.ts @@ -0,0 +1,38 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Tuesday, 1st April 2025 4:57:08 pm + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Uri } from "vscode"; +import { getIdfSetups } from "../eim/getExistingSetups"; +import { isIdfSetupValid } from "../eim/verifySetup"; +import { reportObj } from "./types"; + +export async function checkIDFSetups(reportedResult: reportObj, workspaceFolder: Uri) { + const idfSetups = await getIdfSetups(workspaceFolder); + + for (const idfSetup of idfSetups) { + const extendedIdfSetup = { + ...idfSetup, + reason: "", + }; + [extendedIdfSetup.isValid, extendedIdfSetup.reason] = await isIdfSetupValid( + idfSetup, + false + ); + reportedResult.espIdfSetups.push(extendedIdfSetup); + } +} diff --git a/src/support/checkSpacesInSettings.ts b/src/support/checkSpacesInSettings.ts index 0109b4be1..47f17c419 100644 --- a/src/support/checkSpacesInSettings.ts +++ b/src/support/checkSpacesInSettings.ts @@ -40,10 +40,6 @@ export function checkSpacesInSettings(reportedResult: reportObj) { reportedResult.configurationSettings.espHomeKitPath ); - reportedResult.configurationSpacesValidation.sysPythonBinPath = checkSpacesInPath( - reportedResult.configurationSettings.sysPythonBinPath - ); - reportedResult.configurationSpacesValidation.pythonBinPath = checkSpacesInPath( reportedResult.configurationSettings.pythonBinPath ); diff --git a/src/support/configurationAccess.ts b/src/support/configurationAccess.ts index 5b9db7e06..4871433ab 100644 --- a/src/support/configurationAccess.ts +++ b/src/support/configurationAccess.ts @@ -50,10 +50,6 @@ export async function getConfigurationAccess( reportedResult.configurationSettings.espHomeKitPath, constants.R_OK ); - reportedResult.configurationAccess.sysPythonBinPath = canAccessFile( - reportedResult.configurationSettings.sysPythonBinPath, - constants.X_OK - ); reportedResult.configurationAccess.pythonBinPath = canAccessFile( reportedResult.configurationSettings.pythonBinPath, constants.X_OK diff --git a/src/support/configurationSettings.ts b/src/support/configurationSettings.ts index dbb6567a9..6fced2336 100644 --- a/src/support/configurationSettings.ts +++ b/src/support/configurationSettings.ts @@ -16,10 +16,9 @@ * limitations under the License. */ import { join } from "path"; -import { IdfToolsManager } from "../idfToolsManager"; -import { getEnvVarsFromIdfTools, getPythonEnvPath } from "../pythonManager"; import { reportObj } from "./types"; import { Uri, workspace } from "vscode"; +import { ESP } from "../config"; export async function getConfigurationSettings( reportedResult: reportObj, @@ -30,69 +29,49 @@ export async function getConfigurationSettings( reportedResult.workspaceFolder = scope ? scope.fsPath : "No workspace folder is open"; - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - conf.get("idf.espIdfPath" + winFlag) - ); - const extraPaths = await idfToolsManager.exportPathsInString( - join(conf.get("idf.toolsPath" + winFlag), "tools"), - ["cmake", "ninja"] - ); - const customVars = await idfToolsManager.exportVars( - join(conf.get("idf.toolsPath" + winFlag), "tools") - ); - const pythonVenvPath = await getPythonEnvPath( - conf - .get("idf.espIdfPath" + winFlag) - .replace("${env:IDF_PATH}", process.env.IDF_PATH), - conf - .get("idf.toolsPath" + winFlag) - .replace("${env:IDF_TOOLS_PATH}", process.env.IDF_TOOLS_PATH), - conf.get("idf.pythonInstallPath") - ); + const currentEnvVars = ESP.ProjectConfiguration.store.get<{ + [key: string]: string; + }>(ESP.ProjectConfiguration.CURRENT_IDF_CONFIGURATION, {}); - const idfToolsExportVars = await getEnvVarsFromIdfTools( - conf - .get("idf.espIdfPath" + winFlag) - .replace("${env:IDF_PATH}", process.env.IDF_PATH), - conf - .get("idf.toolsPath" + winFlag) - .replace("${env:IDF_TOOLS_PATH}", process.env.IDF_TOOLS_PATH), - pythonVenvPath - ); + const customExtraVars = conf.get("idf.customExtraVars") as { + [key: string]: string; + }; + const idfPathDir = currentEnvVars["IDF_PATH"] || process.env.IDF_PATH; + const idfToolsPath = + currentEnvVars["IDF_TOOLS_PATH"] || process.env.IDF_TOOLS_PATH; - if (idfToolsExportVars) { - for (const envVar in idfToolsExportVars) { - if (envVar) { - customVars[envVar] = idfToolsExportVars[envVar]; - } - } - } + const pyDir = + process.platform === "win32" + ? ["Scripts", "python.exe"] + : ["bin", "python3"]; + const idfPythonEnvPath = + currentEnvVars["IDF_PYTHON_ENV_PATH"] || process.env.IDF_PYTHON_ENV_PATH; + const venvPythonPath = join(idfPythonEnvPath, ...pyDir); reportedResult.configurationSettings = { - espAdfPath: conf.get("idf.espAdfPath" + winFlag), - espIdfPath: conf.get("idf.espIdfPath" + winFlag), - espMdfPath: conf.get("idf.espMdfPath" + winFlag), - espMatterPath: conf.get("idf.espMatterPath"), - espHomeKitPath: conf.get("idf.espHomeKitSdkPath" + winFlag), + espAdfPath: customExtraVars["ADF_PATH"], + espIdfPath: idfPathDir, + espMdfPath: customExtraVars["MDF_PATH"], + espMatterPath: customExtraVars["ESP_MATTER_PATH"], + espHomeKitPath: customExtraVars["HOMEKIT_PATH"], customTerminalExecutable: conf.get("idf.customTerminalExecutable"), customTerminalExecutableArgs: conf.get("idf.customTerminalExecutableArgs"), flashType: conf.get("idf.flashType"), flashPartitionToUse: conf.get("idf.flashPartitionToUse"), - customExtraPaths: extraPaths, - idfExtraVars: customVars, - userExtraVars: conf.get("idf.customExtraVars"), + customExtraPaths: currentEnvVars["PATH"], + idfExtraVars: currentEnvVars, + userExtraVars: customExtraVars, notificationMode: conf.get("idf.notificationMode"), - pythonBinPath: pythonVenvPath, + pythonBinPath: venvPythonPath, pythonPackages: [], serialPort: conf.get("idf.port" + winFlag), openOCDDebugLevel: conf.get("idf.openOcdDebugLevel") || "2", openOcdConfigs: conf.get("idf.openOcdConfigs") || [], openOcdLaunchArgs: conf.get("idf.openOcdLaunchArgs") || [], - toolsPath: conf.get("idf.toolsPath" + winFlag), + toolsPath: idfToolsPath, systemEnvPath: process.platform === "win32" ? process.env.Path : process.env.PATH, - sysPythonBinPath: conf.get("idf.pythonInstallPath"), gitPath: conf.get("idf.gitPath" + winFlag), }; } diff --git a/src/support/index.ts b/src/support/index.ts index 487cec995..4b7df376b 100644 --- a/src/support/index.ts +++ b/src/support/index.ts @@ -27,7 +27,6 @@ import { getPipVersion } from "./pipVersion"; import { getPythonPackages } from "./pythonPackages"; import { checkEspIdfTools } from "./checkEspIdfTools"; import { checkEspIdfRequirements } from "./checkEspIdfRequirements"; -import { checkDebugAdapterRequirements } from "./checkExtensionRequirements"; import { writeTextReport } from "./writeReport"; import { checkSystemInfo } from "./checkSystemInfo"; import { checkCCppPropertiesJson, checkLaunchJson } from "./checkVscodeFiles"; @@ -36,28 +35,98 @@ import { getProjectConfigurations, getSelectedProjectConfiguration, } from "./projectConfiguration"; +import { checkIDFSetups } from "./checkIdfSetups"; export async function generateConfigurationReport( context: vscode.ExtensionContext, currentWorkspace: vscode.Uri, - reportedResult: reportObj + reportedResult: reportObj, + progress: vscode.Progress<{ message: string; increment: number }> ) { + progress.report({ + message: "Generating configuration report...", + increment: 0, + }); await getConfigurationSettings(reportedResult, currentWorkspace); + progress.report({ + message: "Checking system information...", + increment: 7, + }); await checkSystemInfo(reportedResult); + progress.report({ + message: "Checking configuration access...", + increment: 13, + }); await getConfigurationAccess(reportedResult, context); + progress.report({ + message: "Checking spaces in settings...", + increment: 20, + }); checkSpacesInSettings(reportedResult); + progress.report({ + message: "Checking git version...", + increment: 27, + }); await getGitVersion(reportedResult, context); + progress.report({ + message: "Checking ESP-IDF version...", + increment: 33, + }); await getEspIdfVersion(reportedResult); + progress.report({ + message: "Checking Python version...", + increment: 40, + }); await getPythonVersion(reportedResult, context); + progress.report({ + message: "Checking pip version...", + increment: 47, + }); await getPipVersion(reportedResult, context); + progress.report({ + message: "Checking Python packages...", + increment: 53, + }); await getPythonPackages(reportedResult, context); + progress.report({ + message: "Checking ESP-IDF tools...", + increment: 60, + }); await checkEspIdfTools(reportedResult, context); + progress.report({ + message: "Checking ESP-IDF requirements...", + increment: 67, + }); await checkEspIdfRequirements(reportedResult, context); - await checkDebugAdapterRequirements(reportedResult, context); + progress.report({ + message: "Checking Visual Studio Code files...", + increment: 73, + }); await checkLaunchJson(reportedResult, currentWorkspace); + progress.report({ + message: "Checking C/C++ properties...", + increment: 80, + }); await checkCCppPropertiesJson(reportedResult, currentWorkspace); + progress.report({ + message: "Checking project configurations...", + increment: 87, + }); getProjectConfigurations(reportedResult); + progress.report({ + message: "Getting selected project configuration...", + increment: 93, + }); getSelectedProjectConfiguration(reportedResult); + progress.report({ + message: "Checking ESP-IDF setups...", + increment: 97, + }); + await checkIDFSetups(reportedResult, currentWorkspace); + progress.report({ + message: "Generating report...", + increment: 100, + }); const reportOutput = await writeTextReport(reportedResult, context); await vscode.env.clipboard.writeText(reportOutput); reportedResult.formatedOutput = reportOutput; diff --git a/src/support/initReportObj.ts b/src/support/initReportObj.ts index b3fa43727..a2e1b2c30 100644 --- a/src/support/initReportObj.ts +++ b/src/support/initReportObj.ts @@ -37,7 +37,6 @@ export function initializeReportObject() { pythonBinPath: undefined, pythonPackages: undefined, serialPort: undefined, - sysPythonBinPath: undefined, openOcdConfigs: undefined, openOCDDebugLevel: undefined, openOcdLaunchArgs: undefined, @@ -56,7 +55,6 @@ export function initializeReportObject() { cmakeInEnv: undefined, ninjaInEnv: undefined, toolsPath: undefined, - sysPythonBinPath: undefined, }; report.configurationSpacesValidation = { customExtraPaths: undefined, @@ -69,12 +67,8 @@ export function initializeReportObject() { pythonBinPath: undefined, toolsPath: undefined, systemEnvPath: undefined, - sysPythonBinPath: undefined, - }; - report.debugAdapterRequirements = { - output: undefined, - result: undefined, }; + report.espIdfSetups = []; report.espIdfToolsVersions = undefined; report.espIdfVersion = { output: undefined, diff --git a/src/support/projectConfiguration.ts b/src/support/projectConfiguration.ts index d5865e196..87beb3481 100644 --- a/src/support/projectConfiguration.ts +++ b/src/support/projectConfiguration.ts @@ -20,7 +20,7 @@ import { ESP } from "../config"; import { ProjectConfElement } from "../project-conf/projectConfiguration"; import { reportObj } from "./types"; -export async function getProjectConfigurations(reportedResult: reportObj) { +export function getProjectConfigurations(reportedResult: reportObj) { const currentProjectConfKey = ESP.ProjectConfiguration.store.get( ESP.ProjectConfiguration.SELECTED_CONFIG ); diff --git a/src/support/troubleshootPanel.ts b/src/support/troubleshootPanel.ts index ee8610958..61997b9da 100644 --- a/src/support/troubleshootPanel.ts +++ b/src/support/troubleshootPanel.ts @@ -141,12 +141,12 @@ export class TroubleshootingPanel { { cancellable: false, location: progressLoc, - title: l10n.t("ESP-IDF: Preparing ESP-IDF extension report"), + title: l10n.t("ESP-IDF Doctor"), }, async (progress: Progress<{ message: string; increment: number }>) => { const reportedResult = initializeReportObject(); try { - await generateConfigurationReport(context, workspace, reportedResult); + await generateConfigurationReport(context, workspace, reportedResult, progress); const reportOutput = await writeTextReport(reportedResult, context); troubleshootOutput += reportOutput; await env.clipboard.writeText(troubleshootOutput); diff --git a/src/support/types.ts b/src/support/types.ts index d6ed11c24..180677059 100644 --- a/src/support/types.ts +++ b/src/support/types.ts @@ -16,6 +16,7 @@ * limitations under the License. */ +import { IdfSetup } from "../eim/types"; import { ProjectConfElement } from "../project-conf/projectConfiguration"; export class ConfigurationAccess { @@ -29,7 +30,6 @@ export class ConfigurationAccess { cmakeInEnv: boolean; ninjaInEnv: boolean; toolsPath: boolean; - sysPythonBinPath: boolean; } export class Configuration { systemEnvPath: string; @@ -47,7 +47,6 @@ export class Configuration { pythonBinPath: string; pythonPackages: pyPkgVersion[]; serialPort: string; - sysPythonBinPath: string; openOcdLaunchArgs: string[]; openOcdConfigs: string[]; openOCDDebugLevel: string; @@ -66,7 +65,6 @@ export class ConfigurationSpacesValidation { espHomeKitPath: boolean; customExtraPaths: { [key: string]: boolean }; pythonBinPath: boolean; - sysPythonBinPath: boolean; toolsPath: boolean; gitPath: boolean; } @@ -95,7 +93,7 @@ export class idfToolResult { actual: string; doesToolExist: boolean; expected: string; - id: string; + name: string; } export class execResult { @@ -103,10 +101,15 @@ export class execResult { result: string; } +export interface ExtendedIdfSetup extends IdfSetup { + reason: string; +} + export class reportObj { configurationSettings: Configuration; configurationAccess: ConfigurationAccess; configurationSpacesValidation: ConfigurationSpacesValidation; + espIdfSetups: ExtendedIdfSetup[]; espIdfToolsVersions: idfToolResult[]; espIdfVersion: execResult; gitVersion: execResult; @@ -118,7 +121,6 @@ export class reportObj { pythonVersion: execResult; pythonPackages: execResult; idfCheckRequirements: execResult; - debugAdapterRequirements: execResult; formatedOutput: string; selectedProjectConfiguration: string; systemInfo: SystemInfo; diff --git a/src/support/writeReport.ts b/src/support/writeReport.ts index ac4a49416..08145ad8d 100644 --- a/src/support/writeReport.ts +++ b/src/support/writeReport.ts @@ -41,17 +41,17 @@ export async function writeTextReport( output += `ESP-IDF Extension version ${reportedResult.systemInfo.extensionVersion} ${EOL}`; output += `Workspace folder ${reportedResult.workspaceFolder} ${EOL}`; output += `---------------------------------------------------- Extension configuration settings ------------------------------------------------------${EOL}`; - output += `ESP-ADF Path (idf.espAdfPath) ${reportedResult.configurationSettings.espAdfPath}${EOL}`; - output += `ESP-IDF Path (idf.espIdfPath) ${reportedResult.configurationSettings.espIdfPath}${EOL}`; - output += `ESP-MDF Path (idf.espMdfPath) ${reportedResult.configurationSettings.espMdfPath}${EOL}`; - output += `ESP-Matter Path (idf.espMatterPath) ${reportedResult.configurationSettings.espMatterPath}${EOL}`; - output += `ESP-HomeKit-SDK Path (idf.espHomeKitSdkPath) ${reportedResult.configurationSettings.espHomeKitPath}${EOL}`; + output += `ESP-ADF Path (idf.customExtraVars["ADF_PATH"]) ${reportedResult.configurationSettings.espAdfPath}${EOL}`; + output += `ESP-IDF Path (Project setup IDF_PATH) ${reportedResult.configurationSettings.espIdfPath}${EOL}`; + output += `ESP-MDF Path (idf.customExtraVars["MDF_PATH"]) ${reportedResult.configurationSettings.espMdfPath}${EOL}`; + output += `ESP-Matter Path (idf.customExtraVars["ESP_MATTER_PATH"]) ${reportedResult.configurationSettings.espMatterPath}${EOL}`; + output += `ESP-HomeKit-SDK Path (idf.customExtraVars["HOMEKIT_PATH"]) ${reportedResult.configurationSettings.espHomeKitPath}${EOL}`; output += `Custom extra paths ${reportedResult.configurationSettings.customExtraPaths}${EOL}`; if ( reportedResult.configurationSettings.idfExtraVars && Object.keys(reportedResult.configurationSettings.idfExtraVars) ) { - output += `ESP-IDF extra vars${EOL}`; + output += `ESP-IDF Project Setup Variables${EOL}`; for (let key in reportedResult.configurationSettings.idfExtraVars) { output += ` ${key}: ${reportedResult.configurationSettings.idfExtraVars[key]}${EOL}`; } @@ -65,13 +65,12 @@ export async function writeTextReport( output += ` ${key}: ${reportedResult.configurationSettings.userExtraVars[key]}${EOL}`; } } - output += `System python Path (idf.pythonInstallPath) ${reportedResult.configurationSettings.sysPythonBinPath}${EOL}`; output += `Virtual environment Python path (computed) ${reportedResult.configurationSettings.pythonBinPath}${EOL}`; output += `Serial port (idf.port) ${reportedResult.configurationSettings.serialPort}${EOL}`; output += `OpenOCD Configs (idf.openOcdConfigs) ${reportedResult.configurationSettings.openOcdConfigs}${EOL}`; output += `OpenOCD log level (idf.openOcdDebugLevel) ${reportedResult.configurationSettings.openOCDDebugLevel}${EOL}`; output += `OpenOCD launch arguments (idf.openOcdLaunchArgs) ${reportedResult.configurationSettings.openOcdLaunchArgs}${EOL}`; - output += `ESP-IDF Tools Path (idf.toolsPath) ${reportedResult.configurationSettings.toolsPath}${EOL}`; + output += `ESP-IDF Tools Path ${reportedResult.configurationSettings.toolsPath}${EOL}`; output += `Git Path (idf.gitPath) ${reportedResult.configurationSettings.gitPath}${EOL}`; output += `Notification Mode (idf.notificationMode) ${reportedResult.configurationSettings.notificationMode}${EOL}`; output += `Flash type (idf.flashType) ${reportedResult.configurationSettings.flashType}${EOL}`; @@ -86,35 +85,33 @@ export async function writeTextReport( output += `Custom terminal executable args (idf.customTerminalExecutableArgs)${reportedResult.configurationSettings.customTerminalExecutableArgs}${EOL}`; } output += `-------------------------------------------------------- Configurations access -------------------------------------------------------------${EOL}`; - output += `Access to ESP-ADF Path (idf.espAdfPath) ${reportedResult.configurationAccess.espAdfPath}${EOL}`; - output += `Access to ESP-IDF Path (idf.espIdfPath) ${reportedResult.configurationAccess.espIdfPath}${EOL}`; - output += `Access to ESP-MDF Path (idf.espMdfPath) ${reportedResult.configurationAccess.espMdfPath}${EOL}`; - output += `Access to ESP-Matter Path (idf.espMatterPath) ${reportedResult.configurationAccess.espMatterPath}${EOL}`; - output += `Access to ESP-HomeKit Path (idf.espHomeKitSdkPath) ${reportedResult.configurationAccess.espHomeKitPath}${EOL}`; + output += `Access to ESP-ADF Path (idf.customExtraVars["ADF_PATH"]) ${reportedResult.configurationAccess.espAdfPath}${EOL}`; + output += `Access to ESP-IDF Path (Project setup IDF_PATH) ${reportedResult.configurationAccess.espIdfPath}${EOL}`; + output += `Access to ESP-MDF Path (idf.customExtraVars["MDF_PATH"]) ${reportedResult.configurationAccess.espMdfPath}${EOL}`; + output += `Access to ESP-Matter Path (idf.customExtraVars["ESP_MATTER_PATH"]) ${reportedResult.configurationAccess.espMatterPath}${EOL}`; + output += `Access to ESP-HomeKit Path (idf.customExtraVars["HOMEKIT_PATH"]) ${reportedResult.configurationAccess.espHomeKitPath}${EOL}`; output += `Access to ESP-IDF Custom extra paths${EOL}`; for (let key in reportedResult.configurationAccess.espIdfToolsPaths) { output += `Access to ${key}: ${reportedResult.configurationAccess.espIdfToolsPaths[key]}${EOL}`; } - output += `Access to System python Path (idf.pythonInstallPath) ${reportedResult.configurationAccess.sysPythonBinPath}${EOL}`; output += `Access to Virtual environment Python path (computed) ${reportedResult.configurationAccess.pythonBinPath}${EOL}`; output += `Access to CMake in environment PATH ${reportedResult.configurationAccess.cmakeInEnv}${EOL}`; output += `Access to Ninja in environment PATH ${reportedResult.configurationAccess.ninjaInEnv}${EOL}`; - output += `Access to ESP-IDF Tools Path (idf.toolsPath) ${reportedResult.configurationAccess.toolsPath}${EOL}`; + output += `Access to ESP-IDF Tools Path ${reportedResult.configurationAccess.toolsPath}${EOL}`; output += `-------------------------------------------------------- Configurations has spaces -------------------------------------------------------------${EOL}`; output += `Spaces in system environment Path ${reportedResult.configurationSpacesValidation.systemEnvPath}${EOL}`; - output += `Spaces in ESP-ADF Path (idf.espAdfPath) ${reportedResult.configurationSpacesValidation.espAdfPath}${EOL}`; - output += `Spaces in ESP-IDF Path (idf.espIdfPath) ${reportedResult.configurationSpacesValidation.espIdfPath}${EOL}`; - output += `Spaces in ESP-MDF Path (idf.espMdfPath) ${reportedResult.configurationSpacesValidation.espMdfPath}${EOL}`; - output += `Spaces in ESP-Matter Path (idf.espMatterPath) ${reportedResult.configurationSpacesValidation.espMatterPath}${EOL}`; - output += `Spaces in ESP-HomeKit-SDK Path (idf.espHomeKitSdkPath) ${reportedResult.configurationSpacesValidation.espHomeKitPath}${EOL}`; + output += `Spaces in ESP-ADF Path (idf.customExtraVars["ADF_PATH"]) ${reportedResult.configurationSpacesValidation.espAdfPath}${EOL}`; + output += `Spaces in ESP-IDF Path (Project setup IDF_PATH) ${reportedResult.configurationSpacesValidation.espIdfPath}${EOL}`; + output += `Spaces in ESP-MDF Path (idf.customExtraVars["MDF_PATH"]) ${reportedResult.configurationSpacesValidation.espMdfPath}${EOL}`; + output += `Spaces in ESP-Matter Path (idf.customExtraVars["ESP_MATTER_PATH"]) ${reportedResult.configurationSpacesValidation.espMatterPath}${EOL}`; + output += `Spaces in ESP-HomeKit-SDK Path (idf.customExtraVars["HOMEKIT_PATH"]) ${reportedResult.configurationSpacesValidation.espHomeKitPath}${EOL}`; output += `Spaces in ESP-IDF Custom extra paths${EOL}`; for (let key in reportedResult.configurationSpacesValidation .customExtraPaths) { output += `Spaces in ${key}: ${reportedResult.configurationSpacesValidation.customExtraPaths[key]}${EOL}`; } - output += `Spaces in System python Path (idf.pythonInstallPath) ${reportedResult.configurationSpacesValidation.sysPythonBinPath}${EOL}`; output += `Spaces in Virtual environment Python path (computed) ${reportedResult.configurationSpacesValidation.pythonBinPath}${EOL}`; - output += `Spaces in ESP-IDF Tools Path (idf.toolsPath) ${reportedResult.configurationSpacesValidation.toolsPath}${EOL}`; + output += `Spaces in ESP-IDF Tools Path ${reportedResult.configurationSpacesValidation.toolsPath}${EOL}`; output += `----------------------------------------------------------- Executables Versions -----------------------------------------------------------${EOL}`; output += `Git version ${ reportedResult.gitVersion.result @@ -195,12 +192,27 @@ export async function writeTextReport( ? reportedResult.idfCheckRequirements.result : reportedResult.idfCheckRequirements.output }${EOL}`; - output += `---------------------------------------------------- Check ESP-IDF debug adapter requirements.txt ------------------------------------------${EOL}`; - output += `Check Debug AdapterPython packages ${ - reportedResult.debugAdapterRequirements.result - ? reportedResult.debugAdapterRequirements.result - : reportedResult.debugAdapterRequirements.output - }${EOL}`; + if (reportedResult.espIdfSetups) { + output += `---------------------------------------------------- ESP-IDF Setups ------------------------------------------------------------------------${EOL}`; + for (const idfSetup of reportedResult.espIdfSetups) { + output += `ESP-IDF setup IDF PATH: ${idfSetup.idfPath}${EOL}`; + output += `------- git path: ${idfSetup.gitPath}${EOL}`; + output += `------- IDF_TOOLS_PATH: ${idfSetup.toolsPath}${EOL}`; + output += `------- version: ${idfSetup.version}${EOL}`; + output += `------- python path: ${idfSetup.python}${EOL}`; + if (idfSetup.sysPythonPath) { + output += `------- system python path: ${idfSetup.sysPythonPath}${EOL}`; + } + if (idfSetup.activationScript) { + output += `------- activation script path: ${idfSetup.sysPythonPath}${EOL}`; + } + output += `------- is valid? ${idfSetup.isValid}${EOL}`; + if (idfSetup.reason) { + output += `------- reason: ${idfSetup.reason}${EOL}`; + } + output += `--------------------------------------------------------${EOL}`; + } + } if (reportedResult.launchJson) { output += `---------------------------------------------------- Visual Studio Code launch.json --------------------------------------------------------${EOL}`; output += `${reportedResult.launchJson} ${EOL}`; diff --git a/src/test/adapter.test.ts b/src/test/adapter.test.ts deleted file mode 100644 index 7b428b292..000000000 --- a/src/test/adapter.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Wednesday, 5th June 2019 2:03:34 pm - * Copyright 2019 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as assert from "assert"; -import * as path from "path"; -import { EspIdfDebugClient } from "./espIdfDebugClient"; - -suite("Debug Adapter Tests", () => { - const DEBUG_ADAPTER = path.join( - __dirname, - "..", - "..", - "esp_debug_adapter", - "debug_adapter_main.py" - ); - const portToUse = 43474; // To use in server mode, i.e. start debug adapter yourself - - let debugClient: EspIdfDebugClient; - - setup((done) => { - debugClient = new EspIdfDebugClient( - "python", - ["-u", DEBUG_ADAPTER, "--developer-mode", "connection-check"], - "espidf", - { cwd: __dirname }, - true - ); - // Use portToUse here to attach to existing server. May be easier to debug initially - debugClient.startClient().then(() => done()); - }); - - suite("initialize", () => { - test("should return supported features", async () => { - const initArgs = { - adapterID: "espidf", - clientID: "vscode", - columnsStartAt1: true, - linesStartAt1: true, - }; - await debugClient.initializeRequest(initArgs).then((response) => { - response.body = response.body || {}; - assert.equal(response.body.supportsConfigurationDoneRequest, true); - }); - }).timeout(10000); - }); -}); diff --git a/src/test/doctor.test.ts b/src/test/doctor.test.ts index 6f8dd9194..1e65fb6b9 100644 --- a/src/test/doctor.test.ts +++ b/src/test/doctor.test.ts @@ -31,7 +31,6 @@ import { getConfigurationSettings } from "../support/configurationSettings"; import { readFile, readJSON } from "fs-extra"; import { getPipVersion } from "../support/pipVersion"; import { checkEspIdfRequirements } from "../support/checkEspIdfRequirements"; -import { checkDebugAdapterRequirements } from "../support/checkExtensionRequirements"; import { checkCCppPropertiesJson, checkLaunchJson, @@ -39,6 +38,9 @@ import { import { getPythonPackages } from "../support/pythonPackages"; import { getGitVersion } from "../support/gitVersion"; import { writeTextReport } from "../support/writeReport"; +import { ProjectConfigStore } from "../project-conf"; +import { createMockMemento } from "./mockUtils"; +import { Logger } from "../logger/logger"; suite("Doctor Command tests", () => { const reportObj = initializeReportObject(); @@ -46,7 +48,11 @@ suite("Doctor Command tests", () => { const mockUpContext: vscode.ExtensionContext = { extensionPath: resolve(__dirname, "..", ".."), asAbsolutePath: absPath, + workspaceState: createMockMemento(), + globalState: createMockMemento(), } as vscode.ExtensionContext; + Logger.init(mockUpContext); + ESP.ProjectConfiguration.store = ProjectConfigStore.init(mockUpContext); setup(async () => { setExtensionContext(mockUpContext); }); @@ -72,9 +78,9 @@ suite("Doctor Command tests", () => { assert.equal(reportObj.systemInfo.appName, vscode.env.appName); }); - test("Wrong access to ESP-IDF path", () => { + test("Wrong access to ESP-IDF path", async () => { reportObj.configurationSettings.espIdfPath = "/some/non-existing-path"; - getConfigurationAccess(reportObj, mockUpContext); + await getConfigurationAccess(reportObj, mockUpContext); assert.equal(reportObj.configurationAccess.espIdfPath, false); }); @@ -84,9 +90,9 @@ suite("Doctor Command tests", () => { assert.equal(reportObj.espIdfVersion.result, "x.x"); }); - test("Wrong access to Python path", () => { + test("Wrong access to Python path", async () => { reportObj.configurationSettings.pythonBinPath = "/some/non-existing-path"; - getConfigurationAccess(reportObj, mockUpContext); + await getConfigurationAccess(reportObj, mockUpContext); assert.equal(reportObj.configurationAccess.pythonBinPath, false); }); @@ -102,12 +108,6 @@ suite("Doctor Command tests", () => { assert.equal(reportObj.pipVersion.result, "Not found"); }); - test("Wrong debug adapter py requirements", async () => { - reportObj.configurationSettings.pythonBinPath = "/my/wrong/python/path"; - await checkDebugAdapterRequirements(reportObj, mockUpContext); - assert.equal(reportObj.debugAdapterRequirements.result, "Error"); - }); - test("Wrong esp-idf py requirements", async () => { reportObj.configurationSettings.pythonBinPath = "/my/wrong/python/path"; await checkEspIdfRequirements(reportObj, mockUpContext); @@ -146,18 +146,6 @@ suite("Doctor Command tests", () => { reportObj, vscode.Uri.file(join(__dirname, "../../testFiles/testWorkspace")) ); - assert.equal( - reportObj.configurationSettings.espAdfPath, - settingsJsonObj["idf.espAdfPath"] - ); - assert.equal( - reportObj.configurationSettings.espIdfPath, - settingsJsonObj["idf.espIdfPath"] - ); - assert.equal( - reportObj.configurationSettings.espMdfPath, - settingsJsonObj["idf.espMdfPath"] - ); assert.equal( reportObj.configurationSettings.serialPort, settingsJsonObj["idf.port"] @@ -166,36 +154,19 @@ suite("Doctor Command tests", () => { reportObj.configurationSettings.openOcdConfigs, settingsJsonObj["idf.openOcdConfigs"] ); - assert.equal( - reportObj.configurationSettings.toolsPath, - settingsJsonObj["idf.toolsPath"] - ); assert.equal( reportObj.configurationSettings.notificationMode, settingsJsonObj["idf.notificationMode"] ); }); - test("Good debug adapter py requirements", async () => { - reportObj.configurationSettings.pythonBinPath = `${process.env.IDF_PYTHON_ENV_PATH}/bin/python`; - reportObj.configurationSettings.espIdfPath = process.env.IDF_PATH; - await checkDebugAdapterRequirements(reportObj, mockUpContext); - assert.equal( - reportObj.debugAdapterRequirements.result, - `Python requirements from ${join( - __dirname, - "../../esp_debug_adapter/requirements.txt" - )} are satisfied.` - ); - }); - test("Good esp-idf py requirements", async () => { reportObj.configurationSettings.pythonBinPath = `${process.env.IDF_PYTHON_ENV_PATH}/bin/python`; reportObj.configurationSettings.espIdfPath = process.env.IDF_PATH; await checkEspIdfRequirements(reportObj, mockUpContext); assert.equal( reportObj.idfCheckRequirements.result, - `Python requirements from ${process.env.IDF_PATH}/requirements.txt are satisfied.` + `Python requirements are satisfied.` ); }); @@ -206,7 +177,7 @@ suite("Doctor Command tests", () => { delimiter + process.env.OLD_PATH, "" ); - getConfigurationAccess(reportObj, mockUpContext); + await getConfigurationAccess(reportObj, mockUpContext); assert.equal(reportObj.configurationAccess.pythonBinPath, true); assert.equal(reportObj.configurationAccess.espIdfPath, true); for (let toolPath in reportObj.configurationAccess.espIdfToolsPaths) { @@ -252,6 +223,12 @@ suite("Doctor Command tests", () => { ); }); + function replaceUserPathInStr(strReport: string) { + const escapedHome = process.env.HOME.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const re = new RegExp(escapedHome, "g"); + return strReport.replace(re, ""); + } + test("Match written report", async () => { const customExtraPaths = process.env.PATH.replace( delimiter + process.env.OLD_PATH, @@ -275,17 +252,17 @@ suite("Doctor Command tests", () => { expectedOutput += `ESP-IDF Extension version ${extensionObj.packageJSON.version} ${os.EOL}`; expectedOutput += `Workspace folder ${reportObj.workspaceFolder} ${os.EOL}`; expectedOutput += `---------------------------------------------------- Extension configuration settings ------------------------------------------------------${os.EOL}`; - expectedOutput += `ESP-ADF Path (idf.espAdfPath) ${reportObj.configurationSettings.espAdfPath}${os.EOL}`; - expectedOutput += `ESP-IDF Path (idf.espIdfPath) ${process.env.IDF_PATH}${os.EOL}`; - expectedOutput += `ESP-MDF Path (idf.espMdfPath) ${reportObj.configurationSettings.espMdfPath}${os.EOL}`; - expectedOutput += `ESP-Matter Path (idf.espMatterPath) ${reportObj.configurationSettings.espMatterPath}${os.EOL}`; - expectedOutput += `ESP-HomeKit-SDK Path (idf.espHomeKitSdkPath) ${reportObj.configurationSettings.espHomeKitPath}${os.EOL}`; + expectedOutput += `ESP-ADF Path (idf.customExtraVars["ADF_PATH"]) ${reportObj.configurationSettings.espAdfPath}${os.EOL}`; + expectedOutput += `ESP-IDF Path (Project setup IDF_PATH) ${process.env.IDF_PATH}${os.EOL}`; + expectedOutput += `ESP-MDF Path (idf.customExtraVars["MDF_PATH"]) ${reportObj.configurationSettings.espMdfPath}${os.EOL}`; + expectedOutput += `ESP-Matter Path (idf.customExtraVars["ESP_MATTER_PATH"]) ${reportObj.configurationSettings.espMatterPath}${os.EOL}`; + expectedOutput += `ESP-HomeKit-SDK Path (idf.customExtraVars["HOMEKIT_PATH"]) ${reportObj.configurationSettings.espHomeKitPath}${os.EOL}`; expectedOutput += `Custom extra paths ${customExtraPaths}${os.EOL}`; if ( reportObj.configurationSettings.idfExtraVars && Object.keys(reportObj.configurationSettings.idfExtraVars) ) { - expectedOutput += `ESP-IDF extra vars${os.EOL}`; + expectedOutput += `ESP-IDF Project Setup Variables${os.EOL}`; for (let key in reportObj.configurationSettings.idfExtraVars) { expectedOutput += ` ${key}: ${reportObj.configurationSettings.idfExtraVars[key]}${os.EOL}`; } @@ -299,7 +276,6 @@ suite("Doctor Command tests", () => { expectedOutput += ` ${key}: ${reportObj.configurationSettings.userExtraVars[key]}${os.EOL}`; } } - expectedOutput += `System python Path (idf.pythonInstallPath) ${reportObj.configurationSettings.sysPythonBinPath}${os.EOL}`; expectedOutput += `Virtual environment Python path (computed) ${ process.env.IDF_PYTHON_ENV_PATH + "/bin/python" }${os.EOL}`; @@ -307,11 +283,12 @@ suite("Doctor Command tests", () => { expectedOutput += `OpenOCD Configs (idf.openOcdConfigs) ${reportObj.configurationSettings.openOcdConfigs}${os.EOL}`; expectedOutput += `OpenOCD log level (idf.openOcdDebugLevel) ${reportObj.configurationSettings.openOCDDebugLevel}${os.EOL}`; expectedOutput += `OpenOCD launch arguments (idf.openOcdLaunchArgs) ${reportObj.configurationSettings.openOcdLaunchArgs}${os.EOL}`; - expectedOutput += `ESP-IDF Tools Path (idf.toolsPath) ${reportObj.configurationSettings.toolsPath}${os.EOL}`; + expectedOutput += `ESP-IDF Tools Path ${reportObj.configurationSettings.toolsPath}${os.EOL}`; expectedOutput += `Git Path (idf.gitPath) ${reportObj.configurationSettings.gitPath}${os.EOL}`; expectedOutput += `Notification Mode (idf.notificationMode) ${reportObj.configurationSettings.notificationMode}${os.EOL}`; expectedOutput += `Flash type (idf.flashType) ${reportObj.configurationSettings.flashType}${os.EOL}`; expectedOutput += `Flash partition to use (idf.flashPartitionToUse) ${reportObj.configurationSettings.flashPartitionToUse}${os.EOL}`; + expectedOutput = replaceUserPathInStr(expectedOutput); const actualReport = await writeTextReport(reportObj, mockUpContext); const subReport = actualReport.slice( 0, diff --git a/src/test/espIdfDebugClient.ts b/src/test/espIdfDebugClient.ts deleted file mode 100644 index 93c873963..000000000 --- a/src/test/espIdfDebugClient.ts +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Wednesday, 5th June 2019 2:03:34 pm - * Copyright 2019 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as cp from "child_process"; -import * as net from "net"; -import { ProtocolClient } from "@vscode/debugadapter-testsupport/lib/protocolClient"; -import { DebugProtocol } from "@vscode/debugprotocol"; - -export class EspIdfDebugClient extends ProtocolClient { - private runtime: string; - private execArgs: string[]; - private adapterProcess: cp.ChildProcess; - private spawnOptions: cp.SpawnOptions; - private enableStderr: boolean; - private debugType: string; - private socket: net.Socket; - private defaultPort: number = 43474; - - constructor( - runtime: string, - execArgs: string[], - debugType: string, - spawnOptions: cp.SpawnOptions, - enableStderr: boolean, - defaultPort?: number - ) { - super(); - this.runtime = runtime; - this.execArgs = execArgs; - this.spawnOptions = spawnOptions; - this.enableStderr = enableStderr; - this.debugType = debugType; - if (defaultPort) { - this.defaultPort = defaultPort; - } - } - /** - * Starts a new debug adapter and sets up communication via stdin/stdout. - * When port number is specified, this class will not execute the debug adapter - * and it will connect to specified port directly. - */ - public startClient(port?: number): Promise { - return new Promise((resolve, reject) => { - if (typeof port === "number") { - this.socket = net.createConnection(port, "127.0.0.1", () => { - this.connect(this.socket, this.socket); - resolve(); - }); - } else { - this.adapterProcess = cp.spawn( - this.runtime, - this.execArgs, - this.spawnOptions - ); - const sanitize = (s: string) => s.toString().replace(/\r?\n$/gm, ""); - this.adapterProcess.stderr.on("data", (data: string) => { - // tslint:disable-next-line: no-console - console.log(data.toString()); - if (this.enableStderr) { - // tslint:disable-next-line: no-console - console.log(sanitize(data)); - } - }); - - this.adapterProcess.stdout.on("data", (data: Buffer) => { - // tslint:disable-next-line: no-console - console.log(data.toString()); - if (data.toString().trim().endsWith("DEBUG_ADAPTER_READY2CONNECT")) { - this.socket = net.createConnection( - this.defaultPort, - "127.0.0.1", - () => { - this.connect(this.socket, this.socket); - resolve(); - } - ); - } - }); - - this.adapterProcess.on("error", (err) => { - reject(err); - }); - this.adapterProcess.on("exit", (code: number, signal: string) => { - if (code) { - // tslint:disable-next-line: no-console - console.log(sanitize(`debug adapter exit code: ${code}`)); - } - }); - } - }); - } - - public stop(): Promise { - return this.disconnectRequest() - .then(() => { - this.stopAdapter(); - }) - .catch(() => { - this.stopAdapter(); - }); - } - - public initializeRequest( - args?: DebugProtocol.InitializeRequestArguments - ): Promise { - if (!args) { - args = { - adapterID: this.debugType, - columnsStartAt1: true, - linesStartAt1: true, - pathFormat: "path", - }; - } - return this.send("initialize", args); - } - - public waitForEvent(eventType: string): Promise { - return new Promise((resolve, reject) => { - this.on(eventType, (event) => { - resolve(event); - }); - }); - } - - public waitForResponse(): Promise { - return new Promise((resolve, reject) => { - this.on("responded", (response) => { - resolve(response); - }); - }); - } - - private stopAdapter() { - if (this.adapterProcess) { - this.adapterProcess.kill("SIGKILL"); - this.adapterProcess = null; - } - } - - private disconnectRequest( - args?: DebugProtocol.DisconnectArguments - ): Promise { - return this.send("disconnect", args); - } -} diff --git a/src/test/mockUtils.ts b/src/test/mockUtils.ts new file mode 100644 index 000000000..b84569c33 --- /dev/null +++ b/src/test/mockUtils.ts @@ -0,0 +1,40 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Thursday, 27th February 2025 12:28:08 pm + * Copyright 2025 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Memento } from "vscode"; + +export function createMockMemento(): Memento { + const storage = new Map(); + + return { + get: (key: string, defaultValue?: T): T | undefined => { + return storage.has(key) ? (storage.get(key) as T) : defaultValue; + }, + update: (key: string, value: any): Thenable => { + if (value === undefined) { + storage.delete(key); + } else { + storage.set(key, value); + } + return Promise.resolve(); + }, + keys: (): readonly string[] => { + return Array.from(storage.keys()); + }, + }; +} \ No newline at end of file diff --git a/src/test/project.test.ts b/src/test/project.test.ts index 30c7f3481..58d213219 100644 --- a/src/test/project.test.ts +++ b/src/test/project.test.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Wednesday, 21st July 2021 12:43:10 pm * Copyright 2021 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,14 +29,20 @@ import { setExtensionContext, updateProjectNameInCMakeLists, } from "../utils"; -import { IdfSetup } from "../views/setup/types"; +import { IdfSetup } from "../eim/types"; +import { ProjectConfigStore } from "../project-conf"; +import { ESP } from "../config"; +import { createMockMemento } from "./mockUtils"; suite("Project tests", () => { const absPath = (filename) => resolve(__dirname, "..", "..", filename); const mockUpContext: ExtensionContext = { extensionPath: resolve(__dirname, "..", ".."), asAbsolutePath: absPath, + workspaceState: createMockMemento(), + globalState: createMockMemento(), } as ExtensionContext; + ESP.ProjectConfiguration.store = ProjectConfigStore.init(mockUpContext); const templateFolder = join(mockUpContext.extensionPath, "templates"); const wsFolder = process.env.GITHUB_WORKSPACE ? join(process.env.GITHUB_WORKSPACE, "project-test") @@ -78,11 +84,7 @@ suite("Project tests", () => { "xtensa-esp32-elf-gcc", process.env ); - let compilerRelativePath = compilerAbsolutePath.split( - process.env.IDF_TOOLS_PATH - )[1]; - templateCCppPropertiesJsonJson.configurations[0].compilerPath = - "${config:idf.toolsPath}" + compilerRelativePath; + templateCCppPropertiesJsonJson.configurations[0].compilerPath = compilerAbsolutePath; const targetCCppPropertiesJsonJson = await readJson( join(targetFolder, ".vscode", "c_cpp_properties.json") ); @@ -144,27 +146,22 @@ suite("Project tests", () => { const projectPath = join(wsFolder, "new-project"); const settingsJsonPath = join(projectPath, ".vscode", "settings.json"); const settingsJson = await readJson(settingsJsonPath); - assert.equal(settingsJson["idf.espIdfPath"], undefined); const openOcdConfigs = "interface/ftdi/esp32_devkitj_v1.cfg,target/esp32.cfg"; const idfSetup = { idfPath: process.env.IDF_PATH, toolsPath: process.env.IDF_TOOLS_PATH, - sysPythonPath: "python" + python: `${process.env.IDF_PYTHON_ENV_PATH}/bin/python`, } as IdfSetup; const newSettingsJson = await setCurrentSettingsInTemplate( settingsJsonPath, idfSetup, "no port", "esp32", - openOcdConfigs, - Uri.file(projectPath) + Uri.file(wsFolder), + openOcdConfigs ); - assert.equal(newSettingsJson["idf.espIdfPath"], process.env.IDF_PATH); - assert.equal(newSettingsJson["idf.espAdfPath"], "/test/esp-adf"); - assert.equal(newSettingsJson["idf.espMdfPath"], "/test/esp-mdf"); - assert.equal(newSettingsJson["idf.toolsPath"], process.env.IDF_TOOLS_PATH); assert.equal(newSettingsJson["idf.openOcdConfigs"], openOcdConfigs); }); diff --git a/src/test/suite/downloadManager.test.ts b/src/test/suite/downloadManager.test.ts deleted file mode 100644 index a55f100f8..000000000 --- a/src/test/suite/downloadManager.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Wednesday, 5th June 2019 2:03:34 pm - * Copyright 2019 Espressif Systems (Shanghai) CO LTD - *  - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *  - * http://www.apache.org/licenses/LICENSE-2.0 - *  - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as assert from "assert"; -import * as path from "path"; -import { ExtensionContext } from "vscode"; -import { DownloadManager } from "../../downloadManager"; -import { IdfToolsManager } from "../../idfToolsManager"; -import { InstallManager } from "../../installManager"; -import { IPackage } from "../../IPackage"; -import { Logger } from "../../logger/logger"; -import { OutputChannel } from "../../logger/outputChannel"; -import { PlatformInformation } from "../../PlatformInformation"; -import * as utils from "../../utils"; - -suite("Download Manager Tests", () => { - // Common setup variables - const packageJsonMockUp = JSON.parse(`{ - "tools": [{ - "description": "Ninja build system", - "export_paths": [ - [ - "" - ] - ], - "export_vars": {}, - "install": "on_request", - "name": "ninja", - "platform_overrides": [ - { - "install": "always", - "platforms": [ - "win32", - "win64" - ] - } - ], - "version_cmd": [ - "ninja", - "--version" - ], - "version_regex": "([0-9.]+)", - "versions": [ - { - "linux-amd64": { - "sha256": "978fd9e26c2db8d33392c6daef50e9edac0a3db6680710a9f9ad47e01f3e49b7", - "size": 85276, - "url": "https://dl.espressif.com/dl/test.zip" - }, - "macos": { - "sha256": "9504cd1783ef3c242d06330a50d54dc8f838b605f5fc3e892c47254929f7350c", - "size": 91457, - "url": "https://dl.espressif.com/dl/test.zip" - }, - "name": "1.9.0", - "status": "recommended", - "win64": { - "sha256": "2d70010633ddaacc3af4ffbd21e22fae90d158674a09e132e06424ba3ab036e9", - "size": 254497, - "url": "https://dl.espressif.com/dl/test.zip" - } - } - ] - }]}`); - const absPath = (filename) => path.join(__dirname, filename); - const mockUpContext: ExtensionContext = { - extensionPath: __dirname, - asAbsolutePath: absPath, - } as ExtensionContext; - utils.setExtensionContext(mockUpContext); - Logger.init(mockUpContext); - const output = OutputChannel.init(); - const platInfo: PlatformInformation = { - architecture: "x86_64", - platform: "darwin", - platformToUse: "macos", - } as PlatformInformation; - const mockInstallPath = path.join(__dirname, "../../..", "testFiles"); - const idfToolsManager = new IdfToolsManager( - packageJsonMockUp, - platInfo, - output, - process.env.IDF_PATH - ); - const downloadManager = new DownloadManager(mockInstallPath); - const installManager = new InstallManager(mockInstallPath); - - test("Download correct", async function() { - this.timeout(10000); // Increase timeout to 10 seconds - - const pkgs = await idfToolsManager.getPackageList(["ninja"]); - const pkgUrl = idfToolsManager.obtainUrlInfoForPlatform(pkgs[0]); - const destPath = path.resolve(mockInstallPath, "dist"); - - // Mock the downloadWithResume method to return a mock response - const originalDownloadWithResume = downloadManager.downloadWithResume.bind(downloadManager); - downloadManager.downloadWithResume = async () => { - return { - headers: { - "content-length": "12345", - "content-disposition": "attachment; filename=ninja-win.zip", - "content-type": "application/octet-stream", - }, - status: 200 - }; - }; - - try { - const reply = await downloadManager.downloadWithResume(pkgUrl.url, destPath); - assert.equal(reply.headers["content-length"], "12345"); - } finally { - // Restore original method - downloadManager.downloadWithResume = originalDownloadWithResume; - } - }); - - test("Download fail", async function() { - this.timeout(10000); // Increase timeout to 10 seconds - - const pkgs = await idfToolsManager.getPackageList(["ninja"]); - const pkgUrl = idfToolsManager.obtainUrlInfoForPlatform(pkgs[0]); - const destPath = path.resolve(mockInstallPath, "dist"); - - // Mock the downloadWithResume method to throw an error - const originalDownloadWithResume = downloadManager.downloadWithResume.bind(downloadManager); - downloadManager.downloadWithResume = async () => { - throw new Error("HTTP/HTTPS Response Error"); - }; - - try { - await downloadManager.downloadWithResume(pkgUrl.url, destPath); - assert.fail("Expected an error, didn't receive it"); - } catch (reason) { - assert.equal(reason.message, "HTTP/HTTPS Response Error"); - } finally { - // Restore original method - downloadManager.downloadWithResume = originalDownloadWithResume; - } - }); - - test("Validate file checksum", async () => { - const testFile = path.join(mockInstallPath, "dist", "mytest.zip"); - const expectedHash = - "1e6c77c830842fe48c79d62f8aec25f29075551ecf50d4be948f5c197677ce5b"; - const testFileSize = 239; - await utils - .validateFileSizeAndChecksum(testFile, expectedHash, testFileSize) - .then((isValidFile) => { - assert.equal(isValidFile, true); - }); - }); - - test("Install zip", async () => { - const pkg = { - name: "tool", - version_cmd: ["ninja", "--version"], - versions: [ - { - macos: { - url: "https://dl.espressif.com/dl/mytest.zip", - sha256: - "1e6c77c830842fe48c79d62f8aec25f29075551ecf50d4be948f5c197677ce5b", - size: 239, - }, - name: "1.9.0", - status: "recommended", - }, - ], - } as IPackage; - const versionName = idfToolsManager.getVersionToUse(pkg); - const absolutePath: string = installManager.getToolPackagesPath([ - "tools", - pkg.name, - versionName, - ]); - await installManager - .installZipPackage(idfToolsManager, pkg, absolutePath) - .then(() => { - const fileIsExtracted = utils.fileExists( - downloadManager.getToolPackagesPath([ - "tools", - pkg.name, - pkg.versions[0].name, - "sample.txt", - ]) - ); - assert.equal(fileIsExtracted, true); - }); - }); - - test("Install targz", async () => { - const pkg = { - name: "tool", - version_cmd: ["ninja", "--version"], - versions: [ - { - macos: { - url: "https://dl.espressif.com/dl/tartest.tar.gz", - sha256: - "e506ea55c741db7727b8f4e6187d7f6dcc331f58b2e08dbd13b7fc862f7e1afb", - size: 306, - }, - name: "1.9.0", - status: "recommended", - }, - ], - } as IPackage; - await installManager - .installTarPackage(idfToolsManager, pkg, "gz") - .then(() => { - const isFileExtracted = utils.fileExists( - downloadManager.getToolPackagesPath([ - "tools", - pkg.name, - pkg.versions[0].name, - "tarsample.txt", - ]) - ); - assert.equal(isFileExtracted, true); - }); - }); -}); diff --git a/src/test/suite/idfToolsManager.test.ts b/src/test/suite/idfToolsManager.test.ts index 19fba3234..94a020479 100644 --- a/src/test/suite/idfToolsManager.test.ts +++ b/src/test/suite/idfToolsManager.test.ts @@ -23,6 +23,9 @@ import { IdfToolsManager } from "../../idfToolsManager"; import { OutputChannel } from "../../logger/outputChannel"; import { PlatformInformation } from "../../PlatformInformation"; import * as utils from "../../utils"; +import { ProjectConfigStore } from "../../project-conf"; +import { ESP } from "../../config"; +import { createMockMemento } from "../mockUtils"; suite("IDF Tools Manager Tests", async () => { // Common setup variables @@ -75,8 +78,11 @@ suite("IDF Tools Manager Tests", async () => { }]}`); const mockUpContext: ExtensionContext = { extensionPath: __dirname, + workspaceState: createMockMemento(), + globalState: createMockMemento(), } as ExtensionContext; utils.setExtensionContext(mockUpContext); + ESP.ProjectConfiguration.store = ProjectConfigStore.init(mockUpContext); const platInfo: PlatformInformation = { architecture: "x86_64", platform: "darwin", diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index d134be2ae..94bb8210e 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Wednesday, 5th June 2019 2:03:34 pm * Copyright 2019 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,24 +18,15 @@ import * as glob from "glob"; import * as Mocha from "mocha"; -import * as ju from "mocha-junit-reporter"; import * as path from "path"; export function run(): Promise { // Create the mocha test const mocha = new Mocha({ ui: "tdd", - reporter: ju, + reporter: "json", reporterOptions: { - mochaFile: path.join( - __dirname, - "..", - "..", - "results", - "test-results.xml" - ), - toConsole: true, - outputs: true, + output: path.join(__dirname, "..", "..", "results", "test-results.json"), }, inlineDiffs: true, }); diff --git a/src/test/suite/nvsPartitionTable.test.ts b/src/test/suite/nvsPartitionTable.test.ts index a98cdbeec..7a0a68ee4 100644 --- a/src/test/suite/nvsPartitionTable.test.ts +++ b/src/test/suite/nvsPartitionTable.test.ts @@ -209,23 +209,40 @@ suite("NVS PartitionTable Suite", () => { }); }); - just64Types.forEach( i => { - test(`Value field invalid numbers for ${i}`, async() => { + just64Types.forEach(i => { + test(`Value field invalid numbers for ${i}`, async () => { + const min = minValues[i]; + const max = maxValues[i]; + let invalidMin: string; + let invalidMax: string; + + if (typeof min === "number") { + invalidMin = `${min - 1}`; + } else { + invalidMin = `${min.minus(1)}`; + } + + if (typeof max === "number") { + invalidMax = `${max + 1}`; + } else { + invalidMax = `${max.plus(1)}`; + } + let invalidRow1 = { key: "0123456789abcde", type: "test", encoding: `${i}`, - value: `${minValues[i] === 0 ? minValues[i] - 1 : (minValues[i]).minus(1)}`, + value: invalidMin, error: "" }; let invalidRow2 = { key: "0123456789abcde", type: "test", encoding: `${i}`, - value: `${(maxValues[i]).plus(1)}`, + value: invalidMax, error: "" }; - + await assert.equal(isInValidRow(invalidRow1), `Out of range for ${i}`); await assert.equal(isInValidRow(invalidRow2), `Out of range for ${i}`); }); diff --git a/src/ui-test/.mocharc-ci.js b/src/ui-test/.mocharc-ci.js new file mode 100644 index 000000000..bc8754bec --- /dev/null +++ b/src/ui-test/.mocharc-ci.js @@ -0,0 +1,5 @@ +module.exports = { + timeout: 99999999, + reporter: "json", + "reporter-option": ["output=./out/ui-test-results.json"] +}; diff --git a/src/ui-test/configure-test.ts b/src/ui-test/configure-test.ts deleted file mode 100644 index e928f063c..000000000 --- a/src/ui-test/configure-test.ts +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Friday, 17th December 2021 2:20:05 pm - * Copyright 2021 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { expect } from "chai"; -import { pathExists, stat } from "fs-extra"; -import { delimiter, dirname, join, sep } from "path"; -import { By, EditorView, WebView, Workbench } from "vscode-extension-tester"; - -describe("Configure extension", () => { - let view: WebView; - const expectedDir = process.env.IDF_PATH - ? process.env.IDF_PATH - : join(process.env.HOME, "esp", "esp-idf"); - - before(async function () { - this.timeout(100000); - await new Promise((res) => setTimeout(res, 10000)); - const notifications = await new Workbench().getNotifications(); - for (let n of notifications) { - await n.dismiss(); - } - await new Workbench().executeCommand("espIdf.setup.start"); - await new Promise((res) => setTimeout(res, 12000)); - view = new WebView(); - await view.switchToFrame(); - }); - - async function isBinInPath( - binaryName: string, - workDirectory: string, - env: NodeJS.ProcessEnv - ) { - let pathNameInEnv: string = Object.keys(process.env).find( - (k) => k.toUpperCase() == "PATH" - ); - const pathDirs = env[pathNameInEnv].split(delimiter); - for (const pathDir of pathDirs) { - let binaryPath = join(pathDir, binaryName); - if (process.platform === "win32" && !binaryName.endsWith(".exe")) { - binaryPath = `${binaryPath}.exe`; - } - const doesPathExists = await pathExists(binaryPath); - if (doesPathExists) { - const pathStats = await stat(binaryPath); - if (pathStats.isFile() && pathStats.mode & 0o111) { - return binaryPath; - } - } - } - return ""; - } - - async function selectEspIdfVersion(view: WebView) { - const selectEspIdfElement = await view.findWebElement( - By.id("select-esp-idf") - ); - const idfChoices = await selectEspIdfElement.findElements(By.css("option")); - expect(idfChoices).to.be.an("array"); - await idfChoices[0].click(); - } - - async function checkGitVersion(view: WebView) { - const gitVersionElement = await view.findWebElement( - By.xpath(`.//label[@data-config-id='git-version']`) - ); - const gitVersionMsg = await gitVersionElement.getText(); - expect(gitVersionMsg).to.match(/Git version:.*/g); - } - - async function checkManualEspIdfPath(view: WebView) { - const manualIdfDirectory = await view.findWebElement( - By.xpath( - `.//div[@data-config-id='manual-idf-directory']//input[@type='text']` - ) - ); - const defaultIdfDirectory = await manualIdfDirectory.getAttribute("value"); - expect(defaultIdfDirectory).to.be.equal(expectedDir); - } - - async function selectPythonExecutable(view: WebView) { - const selectPythonElement = await view.findWebElement( - By.id("python-version-select") - ); - await new Promise((res) => setTimeout(res, 1000)); - const pyChoices = await selectPythonElement.findElements(By.css("option")); - for (const pyChoice of pyChoices) { - const pyText = await pyChoice.getText(); - if (pyText.indexOf("/usr/bin/python3") !== -1) { - await pyChoice.click(); - break; - } - } - // Start setup install - const startInstallBtn = await view.findWebElement( - By.xpath(`.//button[@data-config-id='start-install-btn']`) - ); - await startInstallBtn.click(); - await new Promise((res) => setTimeout(res, 3000)); - } - - it("Find install options", async () => { - const expressElement = await view.findWebElement( - By.xpath(`.//label[@data-config-id='express']`) - ); - expect(await expressElement.getText()).has.string("EXPRESS"); - - const advancedElement = await view.findWebElement( - By.xpath(`.//label[@data-config-id='advanced']`) - ); - expect(await advancedElement.getText()).has.string("ADVANCED"); - - const existingElement = await view.findWebElement( - By.xpath(`.//label[@data-config-id='existing-setup']`) - ); - expect(await existingElement.getText()).has.string("USE EXISTING SETUP"); - }); - - it("Configure using Express", async () => { - const expressElement = await view.findWebElement( - By.id("express-install-btn") - ); - await expressElement.click(); - await new Promise((res) => setTimeout(res, 1000)); - - await checkGitVersion(view); - await selectEspIdfVersion(view); - await checkManualEspIdfPath(view); - await selectPythonExecutable(view); - - // Status windows is loaded - const espIdfInstalledPath = await view.findWebElement( - By.xpath(`.//p[@data-config-id='esp-idf-download-status']`) - ); - const espIdfDestPathMsg = await espIdfInstalledPath.getText(); - const expectedEspIdfDestPath = `ESP-IDF is installed in ${expectedDir}`; - expect(espIdfDestPathMsg).to.be.equal(expectedEspIdfDestPath); - - await new Promise((res) => setTimeout(res, 100000)); - - // Show setup has finished - const setupFinishedElement = await view.findWebElement( - By.xpath(`.//h2[@data-config-id='setup-is-finished']`) - ); - const setupFinishedText = await setupFinishedElement.getText(); - expect(setupFinishedText).to.be.equal("All settings have been configured."); - if (view) { - await view.switchBack(); - await new EditorView().closeAllEditors(); - } - }).timeout(120000); - - it("Configure using Advanced", async () => { - await new Workbench().executeCommand("espIdf.setup.start"); - await new Promise((res) => setTimeout(res, 12000)); - view = new WebView(); - await view.switchToFrame(); - await new Promise((res) => setTimeout(res, 1000)); - const advancedElement = await view.findWebElement( - By.id("advanced-install-btn") - ); - await advancedElement.click(); - await new Promise((res) => setTimeout(res, 1000)); - await selectEspIdfVersion(view); - - await checkGitVersion(view); - - await checkManualEspIdfPath(view); - - await selectPythonExecutable(view); - - // select-esp-idf-tools - const idfToolSelect = await view.findWebElement( - By.xpath(`.//select[@data-config-id='select-esp-idf-tools']`) - ); - const idfToolsSelectChoices = await idfToolSelect.findElements( - By.css("option") - ); - await idfToolsSelectChoices[idfToolsSelectChoices.length - 1].click(); - - // Get current settings - const modifiedEnv: { [key: string]: string } = <{ [key: string]: string }>( - Object.assign({}, process.env) - ); - // openOCD-esp32 - const openOCDPath = await isBinInPath("openocd", __dirname, modifiedEnv); - const openOcdTool = await view.findWebElement(By.id("openocd-esp32")); - const expectedOpenOcdPath = openOCDPath - .trim() - .replace(sep + "bin" + sep + "openocd", sep + "bin"); - const actualOpenOcdPath = await openOcdTool.getAttribute("value"); - expect(expectedOpenOcdPath).to.be.equal(actualOpenOcdPath); - // xtensa-esp32-elf/ - const xtensaEsp32Path = await isBinInPath( - "xtensa-esp32-elf-gcc", - __dirname, - modifiedEnv - ); - const xtensaEsp32Tool = await view.findWebElement( - By.id("xtensa-esp-elf") - ); - const expectedXtensaEsp32Path = dirname(xtensaEsp32Path); - const actualXtensaEsp32Path = await xtensaEsp32Tool.getAttribute("value"); - expect(expectedXtensaEsp32Path).to.be.equal(actualXtensaEsp32Path); - - // save-existing-tools - const saveExistingToolsBtn = await view.findWebElement( - By.xpath(`.//button[@data-config-id='save-existing-tools']`) - ); - await saveExistingToolsBtn.click(); - - await new Promise((res) => setTimeout(res, 100000)); - - // Show setup has finished - const setupFinishedElement = await view.findWebElement( - By.xpath(`.//h2[@data-config-id='setup-is-finished']`) - ); - const setupFinishedText = await setupFinishedElement.getText(); - expect(setupFinishedText).to.be.equal("All settings have been configured."); - if (view) { - await view.switchBack(); - await new EditorView().closeAllEditors(); - } - }).timeout(130000); - - it("Configure using existing setup", async () => { - await new Workbench().executeCommand("espIdf.setup.start"); - await new Promise((res) => setTimeout(res, 12000)); - view = new WebView(); - await view.switchToFrame(); - await new Promise((res) => setTimeout(res, 1000)); - const existingSetupElement = await view.findWebElement( - By.id("existing-install-btn") - ); - await existingSetupElement.click(); - await new Promise((res) => setTimeout(res, 1000)); - // Status windows is loaded - const expectedDir = process.env.IDF_PATH - ? process.env.IDF_PATH - : join(process.env.HOME, "esp", "esp-idf"); - const espIdfInstalledPath = await view.findWebElement( - By.xpath(`.//div[@data-config-id='${expectedDir}']`) - ); - const espIdfDestPathMsg = await espIdfInstalledPath.getText(); - expect(espIdfDestPathMsg).to.include(expectedDir); - - await espIdfInstalledPath.click(); - - await new Promise((res) => setTimeout(res, 60000)); - - // Show setup has finished - const setupFinishedElement = await view.findWebElement( - By.xpath(`.//h2[@data-config-id='setup-is-finished']`) - ); - const setupFinishedText = await setupFinishedElement.getText(); - expect(setupFinishedText).to.be.equal("All settings have been configured."); - - if (view) { - await view.switchBack(); - } - }).timeout(100000); -}); diff --git a/src/ui-test/project-build-test.ts b/src/ui-test/project-build-test.ts index 53e1e2112..0dcb41b5f 100644 --- a/src/ui-test/project-build-test.ts +++ b/src/ui-test/project-build-test.ts @@ -16,7 +16,12 @@ * limitations under the License. */ -import { BottomBarPanel, EditorView, InputBox, Workbench } from "vscode-extension-tester"; +import { + BottomBarPanel, + EditorView, + InputBox, + Workbench, +} from "vscode-extension-tester"; import { expect } from "chai"; import { resolve } from "path"; import { pathExists } from "fs-extra"; @@ -26,18 +31,22 @@ describe("Build testing", async () => { before(async function () { this.timeout(100000); + const notifications = await new Workbench().getNotifications(); + for (let n of notifications) { + await n.dismiss(); + } await openTestProject(); }); it("Log Doctor command configuration", async () => { - await new Promise((res) => setTimeout(res, 3000)); - await new Workbench().executeCommand("ESP-IDF: Doctor Command"); - await new Promise((res) => setTimeout(res, 10000)); - const editorView = new EditorView(); - const editor = await editorView.openEditor("report.txt"); - const docCmdText = await editor.getText(); - console.log(docCmdText); - }).timeout(999999); + await new Promise((res) => setTimeout(res, 3000)); + await new Workbench().executeCommand("ESP-IDF: Doctor Command"); + await new Promise((res) => setTimeout(res, 10000)); + const editorView = new EditorView(); + const editor = await editorView.openEditor("report.txt"); + const docCmdText = await editor.getText(); + console.log(docCmdText); + }).timeout(999999); it("Build bin is generated", async () => { await new Workbench().executeCommand("ESP-IDF: Full Clean Project"); diff --git a/src/uninstall.ts b/src/uninstall.ts index 33b533383..bc54f3294 100644 --- a/src/uninstall.ts +++ b/src/uninstall.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode"; import { Logger } from "./logger/logger"; import { OutputChannel } from "./logger/outputChannel"; -import { clearPreviousIdfSetups } from "./setup/existingIdfSetups"; +import { ESP } from "./config"; export async function asyncRemoveEspIdfSettings() { const config = vscode.workspace.getConfiguration(); @@ -134,8 +134,6 @@ export async function asyncRemoveEspIdfSettings() { } } - await clearPreviousIdfSetups(); - OutputChannel.appendLineAndShow( vscode.l10n.t("ESP-IDF settings removed successfully.") ); diff --git a/src/utils.ts b/src/utils.ts index d88f2a5ae..2711fed37 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -42,12 +42,12 @@ import { ESP } from "./config"; import * as sanitizedHtml from "sanitize-html"; import { getEnvVarsFromIdfTools, - getPythonPath, getVirtualEnvPythonPath, } from "./pythonManager"; import { IdfToolsManager } from "./idfToolsManager"; import { isFlashEncryptionEnabled } from "./flash/verifyFlashEncryption"; import { configureClangSettings } from "./clang"; +import { configureEnvVariables } from "./common/prepareEnv"; const currentFolderMsg = vscode.l10n.t("ESP-IDF: Current Project"); @@ -174,6 +174,7 @@ export interface ISpawnOptions extends childProcess.SpawnOptions { appendMode?: "appendLine" | "append"; /** Send error to telemetry */ sendToTelemetry?: boolean; + maxBuffer?: number; } export function spawn( @@ -318,24 +319,17 @@ export async function createGitignoreFile(destinationDir: vscode.Uri) { export async function setCCppPropertiesJsonCompilerPath( curWorkspaceFsPath: vscode.Uri ) { - const modifiedEnv = await appendIdfAndToolsToPath(curWorkspaceFsPath); + const modifiedEnv = await configureEnvVariables(curWorkspaceFsPath); const idfTarget = modifiedEnv.IDF_TARGET || "esp32"; const gccTool = getToolchainToolName(idfTarget, "gcc"); const compilerAbsolutePath = await isBinInPath(gccTool, modifiedEnv); if (!compilerAbsolutePath) { return; } - let compilerRelativePath = compilerAbsolutePath.split( - modifiedEnv.IDF_TOOLS_PATH - )[1]; - const settingToUse = - process.platform === "win32" - ? "${config:idf.toolsPathWin}" - : "${config:idf.toolsPath}"; await updateCCppPropertiesJson( curWorkspaceFsPath, "compilerPath", - settingToUse + compilerRelativePath + compilerAbsolutePath ); } @@ -386,14 +380,14 @@ export async function getToolchainPath( workspaceUri: vscode.Uri, tool: string = "gcc" ) { - const modifiedEnv = await appendIdfAndToolsToPath(workspaceUri); + const modifiedEnv = await configureEnvVariables(workspaceUri); const idfTarget = modifiedEnv.IDF_TARGET || "esp32"; const gccTool = getToolchainToolName(idfTarget, tool); try { return await isBinInPath(gccTool, modifiedEnv); } catch (error) { Logger.errorNotify( - `${tool} is not found in idf.toolsPath`, + `${tool} is not found in current IDF setup`, error, "utils getToolchainPath" ); @@ -846,6 +840,11 @@ export function getSubProjects(dir: string): string[] { } export async function getEspIdfFromCMake(espIdfPath: string) { + const doesIdfPathExists = await pathExists(espIdfPath); + if (!doesIdfPathExists) { + Logger.info(`${espIdfPath} does not exist to get ESP-IDF version.`); + return "x.x"; + } const versionFilePath = path.join( espIdfPath, "tools", @@ -916,7 +915,7 @@ export async function cleanDirtyGitRepository( return; } const workingDirUri = vscode.Uri.file(workingDir); - const modifiedEnv = await appendIdfAndToolsToPath(workingDirUri); + const modifiedEnv = await configureEnvVariables(workingDirUri); const resetResult = await execChildProcess( gitPath, ["reset", "--hard", "--recurse-submodule"], @@ -942,7 +941,7 @@ export async function fixFileModeGitRepository( return; } const workingDirUri = vscode.Uri.file(workingDir); - const modifiedEnv = await appendIdfAndToolsToPath(workingDirUri); + const modifiedEnv = await configureEnvVariables(workingDirUri); const fixFileModeResult = await execChildProcess( gitPath, ["config", "--local", "core.fileMode", "false"], @@ -1030,295 +1029,6 @@ export function validateFileSizeAndChecksum( }); } -export async function appendIdfAndToolsToPath(curWorkspace: vscode.Uri) { - const modifiedEnv: { [key: string]: string } = <{ [key: string]: string }>( - Object.assign({}, process.env) - ); - - const containerPath = - process.platform === "win32" ? modifiedEnv.USERPROFILE : modifiedEnv.HOME; - const defaultEspIdfPath = path.join(containerPath, "esp", "esp-idf"); - - const idfPathDir = idfConf.readParameter("idf.espIdfPath", curWorkspace); - modifiedEnv.IDF_PATH = - idfPathDir || modifiedEnv.IDF_PATH || defaultEspIdfPath; - - const adfPathDir = idfConf.readParameter("idf.espAdfPath", curWorkspace); - modifiedEnv.ADF_PATH = adfPathDir || modifiedEnv.ADF_PATH; - - const mdfPathDir = idfConf.readParameter("idf.espMdfPath", curWorkspace); - modifiedEnv.MDF_PATH = mdfPathDir || modifiedEnv.MDF_PATH; - - const homekitPathDir = idfConf.readParameter( - "idf.espHomeKitSdkPath", - curWorkspace - ); - modifiedEnv.HOMEKIT_PATH = homekitPathDir || modifiedEnv.HOMEKIT_PATH; - - const rainmakerPathDir = idfConf.readParameter( - "idf.espRainmakerPath", - curWorkspace - ); - modifiedEnv.RMAKER_PATH = rainmakerPathDir || modifiedEnv.RMAKER_PATH; - - const defaultToolsPath = path.join(containerPath, ".espressif"); - const toolsPath = idfConf.readParameter( - "idf.toolsPath", - curWorkspace - ) as string; - modifiedEnv.IDF_TOOLS_PATH = toolsPath || defaultToolsPath; - const matterPathDir = idfConf.readParameter( - "idf.espMatterPath", - curWorkspace - ) as string; - modifiedEnv.ESP_MATTER_PATH = matterPathDir || modifiedEnv.ESP_MATTER_PATH; - - const idfToolsManager = await IdfToolsManager.createIdfToolsManager( - modifiedEnv.IDF_PATH - ); - - const extraPaths = await idfToolsManager.exportPathsInString( - path.join(modifiedEnv.IDF_TOOLS_PATH, "tools"), - ["cmake", "ninja"] - ); - const customVars = await idfToolsManager.exportVars( - path.join(modifiedEnv.IDF_TOOLS_PATH, "tools") - ); - - if (customVars) { - try { - for (const envVar in customVars) { - if (envVar) { - modifiedEnv[envVar] = customVars[envVar]; - } - } - } catch (error) { - Logger.errorNotify( - "Invalid ESP-IDF environment variables format", - error, - "appendIdfAndToolsToPath idf tools env vars" - ); - } - } - - let pathToPigweed: string; - - if (modifiedEnv.ESP_MATTER_PATH) { - pathToPigweed = path.join( - modifiedEnv.ESP_MATTER_PATH, - "connectedhomeip", - "connectedhomeip", - ".environment", - "cipd", - "packages", - "pigweed" - ); - modifiedEnv.ZAP_INSTALL_PATH = path.join( - modifiedEnv.ESP_MATTER_PATH, - "connectedhomeip", - "connectedhomeip", - ".environment", - "cipd", - "packages", - "zap" - ); - } - const sysPythonPath = await getPythonPath(curWorkspace); - let pythonBinPath = ""; - if (sysPythonPath) { - pythonBinPath = await getVirtualEnvPythonPath(curWorkspace); - } - if (!pythonBinPath) { - pythonBinPath = idfConf.readParameter( - "idf.pythonBinPath", - curWorkspace - ) as string; - } - modifiedEnv.PYTHON = - pythonBinPath || - `${process.env.PYTHON}` || - `${path.join(process.env.IDF_PYTHON_ENV_PATH, "bin", "python")}`; - - const pythonBinPathExists = await pathExists(pythonBinPath); - - modifiedEnv.IDF_PYTHON_ENV_PATH = pythonBinPathExists - ? path.dirname(path.dirname(pythonBinPath)) - : process.env.IDF_PYTHON_ENV_PATH; - - const gitPath = idfConf.readParameter("idf.gitPath", curWorkspace) as string; - let pathToGitDir; - if (gitPath && gitPath !== "git") { - pathToGitDir = path.dirname(gitPath); - } - - let IDF_ADD_PATHS_EXTRAS = path.join( - modifiedEnv.IDF_PATH, - "components", - "espcoredump" - ); - IDF_ADD_PATHS_EXTRAS = `${IDF_ADD_PATHS_EXTRAS}${path.delimiter}${path.join( - modifiedEnv.IDF_PATH, - "components", - "partition_table" - )}`; - - let pathNameInEnv: string = Object.keys(process.env).find( - (k) => k.toUpperCase() == "PATH" - ); - - const idfPathExists = await pathExists(modifiedEnv.IDF_PATH); - const idfToolsPathExists = await pathExists(modifiedEnv.IDF_TOOLS_PATH); - - if (pythonBinPathExists && idfPathExists && idfToolsPathExists) { - const idfToolsExportVars = await getEnvVarsFromIdfTools( - modifiedEnv.IDF_PATH, - modifiedEnv.IDF_TOOLS_PATH, - pythonBinPath - ); - - if (idfToolsExportVars) { - try { - for (const envVar in idfToolsExportVars) { - if (envVar.toUpperCase() === pathNameInEnv.toUpperCase()) { - modifiedEnv[pathNameInEnv] = idfToolsExportVars[envVar] - .replace("%PATH%", modifiedEnv[pathNameInEnv]) - .replace("$PATH", modifiedEnv[pathNameInEnv]); - } else { - modifiedEnv[envVar] = idfToolsExportVars[envVar]; - } - } - } catch (error) { - Logger.errorNotify( - "Invalid ESP-IDF idf_tools.py export environment variables format", - error, - "appendIdfAndToolsToPath idf_tools export env vars" - ); - } - } - } - - const customExtraVars = idfConf.readParameter( - "idf.customExtraVars", - curWorkspace - ) as { [key: string]: string }; - if (customExtraVars) { - try { - for (const envVar in customExtraVars) { - if (envVar) { - modifiedEnv[envVar] = customExtraVars[envVar]; - } - } - } catch (error) { - Logger.errorNotify( - "Invalid user environment variables format", - error, - "appendIdfAndToolsToPath idf.customExtraVars" - ); - } - } - - try { - const openOcdPath = await isBinInPath("openocd", modifiedEnv, [ - "openocd-esp32", - ]); - if (openOcdPath) { - const openOcdDir = path.dirname(openOcdPath); - const openOcdScriptsPath = path.join( - openOcdDir, - "..", - "share", - "openocd", - "scripts" - ); - const scriptsExists = await pathExists(openOcdScriptsPath); - if (scriptsExists && modifiedEnv.OPENOCD_SCRIPTS !== openOcdScriptsPath) { - modifiedEnv.OPENOCD_SCRIPTS = openOcdScriptsPath; - } - } - } catch (error) { - Logger.error( - `Error processing OPENOCD_SCRIPTS path: ${error.message}`, - error, - "appendIdfAndToolsToPath OPENOCD_SCRIPTS" - ); - } - - if ( - pathToGitDir && - !modifiedEnv[pathNameInEnv].split(path.delimiter).includes(pathToGitDir) - ) { - modifiedEnv[pathNameInEnv] += path.delimiter + pathToGitDir; - } - if ( - pathToPigweed && - !modifiedEnv[pathNameInEnv].split(path.delimiter).includes(pathToPigweed) - ) { - modifiedEnv[pathNameInEnv] += path.delimiter + pathToPigweed; - } - - if ( - modifiedEnv[pathNameInEnv] && - !modifiedEnv[pathNameInEnv].includes(path.dirname(modifiedEnv.PYTHON)) - ) { - modifiedEnv[pathNameInEnv] = - path.dirname(modifiedEnv.PYTHON) + - path.delimiter + - modifiedEnv[pathNameInEnv]; - } - - if ( - modifiedEnv[pathNameInEnv] && - !modifiedEnv[pathNameInEnv].includes( - path.join(modifiedEnv.IDF_PATH, "tools") - ) - ) { - modifiedEnv[pathNameInEnv] = - path.join(modifiedEnv.IDF_PATH, "tools") + - path.delimiter + - modifiedEnv[pathNameInEnv]; - } - - const extraPathsArray = extraPaths.split(path.delimiter); - for (let extraPath of extraPathsArray) { - if ( - modifiedEnv[pathNameInEnv] && - !modifiedEnv[pathNameInEnv].includes(extraPath) - ) { - modifiedEnv[pathNameInEnv] = - extraPath + path.delimiter + modifiedEnv[pathNameInEnv]; - } - } - - modifiedEnv[ - pathNameInEnv - ] = `${IDF_ADD_PATHS_EXTRAS}${path.delimiter}${modifiedEnv[pathNameInEnv]}`; - - let idfTarget = await getIdfTargetFromSdkconfig(curWorkspace); - if (idfTarget) { - modifiedEnv.IDF_TARGET = - modifiedEnv.IDF_TARGET || idfTarget || process.env.IDF_TARGET; - } - - let enableComponentManager = idfConf.readParameter( - "idf.enableIdfComponentManager", - curWorkspace - ) as boolean; - - if (enableComponentManager) { - modifiedEnv.IDF_COMPONENT_MANAGER = "1"; - } - - let sdkconfigFilePath = idfConf.readParameter( - "idf.sdkconfigFilePath", - curWorkspace - ) as string; - if (sdkconfigFilePath) { - modifiedEnv.SDKCONFIG = sdkconfigFilePath; - } - - return modifiedEnv; -} - export async function getAllBinPathInEnvPath( binaryName: string, env: NodeJS.ProcessEnv diff --git a/src/versionSwitcher/index.ts b/src/versionSwitcher/index.ts index de55d61de..9065454d5 100644 --- a/src/versionSwitcher/index.ts +++ b/src/versionSwitcher/index.ts @@ -16,36 +16,24 @@ * limitations under the License. */ -import { ConfigurationTarget, StatusBarItem, Uri, window } from "vscode"; -import { - getPreviousIdfSetups, - loadIdfSetupsFromEspIdfJson, -} from "../setup/existingIdfSetups"; -import { - checkIdfSetup, - useIdfSetupSettings, -} from "../setup/setupValidation/espIdfSetup"; -import { readParameter } from "../idfConfiguration"; -import { getIdfMd5sum } from "../setup/espIdfJson"; -import { getEspIdfFromCMake } from "../utils"; -import { IdfSetup } from "../views/setup/types"; -import { getPythonPath, getVirtualEnvPythonPath } from "../pythonManager"; +import { commands, l10n, StatusBarItem, Uri, window } from "vscode"; +import { getIdfSetups } from "../eim/getExistingSetups"; +import { saveSettings } from "../eim/verifySetup"; export async function selectIdfSetup( workspaceFolder: Uri, espIdfStatusBar: StatusBarItem ) { - const globalStateSetups = await getPreviousIdfSetups(true); - const toolsPath = readParameter("idf.toolsPath", workspaceFolder) as string; - let existingIdfSetups = await loadIdfSetupsFromEspIdfJson(toolsPath); - if (process.env.IDF_TOOLS_PATH && toolsPath !== process.env.IDF_TOOLS_PATH) { - const systemIdfSetups = await loadIdfSetupsFromEspIdfJson( - process.env.IDF_TOOLS_PATH + let idfSetups = await getIdfSetups(workspaceFolder); + if (!idfSetups || (idfSetups && idfSetups.length === 0)) { + const action = await window.showInformationMessage( + l10n.t("No ESP-IDF Setups found"), + l10n.t("Open ESP-IDF Installation Manager") ); - existingIdfSetups = [...existingIdfSetups, ...systemIdfSetups]; + if (action && action === l10n.t("Open ESP-IDF Installation Manager")) { + commands.executeCommand("espIdf.installManager"); + } } - const currentIdfSetup = await getCurrentIdfSetup(workspaceFolder); - let idfSetups = [...globalStateSetups, ...existingIdfSetups, currentIdfSetup]; idfSetups = idfSetups.filter( (setup, index, self) => index === @@ -53,16 +41,7 @@ export async function selectIdfSetup( (s) => s.idfPath === setup.idfPath && s.toolsPath === setup.toolsPath ) ); - if (idfSetups.length === 0) { - await window.showInformationMessage("No ESP-IDF Setups found"); - return; - } - const onlyValidIdfSetups = [ - ...new Map( - idfSetups.filter((i) => i.isValid).map((item) => [item.idfPath, item]) - ).values(), - ]; - const idfSetupOptions = onlyValidIdfSetups.map((idfSetup) => { + const idfSetupOptions = idfSetups.map((idfSetup) => { return { label: `Version: v${idfSetup.version}`, description: `IDF_PATH: ${idfSetup.idfPath}`, @@ -76,49 +55,10 @@ export async function selectIdfSetup( if (!selectedIdfSetupOption) { return; } - await useIdfSetupSettings( + await saveSettings( selectedIdfSetupOption.target, - ConfigurationTarget.WorkspaceFolder, workspaceFolder, espIdfStatusBar ); return selectedIdfSetupOption.target; } - -export async function getCurrentIdfSetup( - workspaceFolder: Uri, - logToChannel: boolean = true -) { - const idfPath = readParameter("idf.espIdfPath", workspaceFolder); - const toolsPath = readParameter("idf.toolsPath", workspaceFolder) as string; - const gitPath = readParameter("idf.gitPath", workspaceFolder); - - // FIX use system Python path as setting instead venv - // REMOVE this line after neext release - const sysPythonBinPath = await getPythonPath(workspaceFolder); - let pythonBinPath = ""; - if (sysPythonBinPath) { - pythonBinPath = await getVirtualEnvPythonPath(workspaceFolder); - } - if (!pythonBinPath) { - pythonBinPath = readParameter( - "idf.pythonBinPath", - workspaceFolder - ) as string; - } - - const idfSetupId = getIdfMd5sum(idfPath); - const idfVersion = await getEspIdfFromCMake(idfPath); - const currentIdfSetup: IdfSetup = { - id: idfSetupId, - idfPath, - gitPath, - toolsPath, - sysPythonPath: sysPythonBinPath, - python: pythonBinPath, - version: idfVersion, - isValid: false, - }; - currentIdfSetup.isValid = await checkIdfSetup(currentIdfSetup, logToChannel); - return currentIdfSetup; -} diff --git a/src/views/commons/espCommons.scss b/src/views/commons/espCommons.scss index 1a5c38f06..8402b0815 100644 --- a/src/views/commons/espCommons.scss +++ b/src/views/commons/espCommons.scss @@ -1,5 +1,5 @@ @charset "utf-8"; -@import "~bulma/bulma"; +@use "~bulma/bulma" as bulma; html, body { diff --git a/src/views/menuconfig/Menuconfig.vue b/src/views/menuconfig/Menuconfig.vue index 1be91034a..675a83723 100644 --- a/src/views/menuconfig/Menuconfig.vue +++ b/src/views/menuconfig/Menuconfig.vue @@ -180,7 +180,7 @@ onUnmounted(() => { diff --git a/src/views/setup/ExistingSetup.vue b/src/views/setup/ExistingSetup.vue deleted file mode 100644 index 475373e55..000000000 --- a/src/views/setup/ExistingSetup.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - -./types \ No newline at end of file diff --git a/src/views/setup/Home.vue b/src/views/setup/Home.vue deleted file mode 100644 index 62f374f52..000000000 --- a/src/views/setup/Home.vue +++ /dev/null @@ -1,191 +0,0 @@ - - - - - -./types diff --git a/src/views/setup/Install.vue b/src/views/setup/Install.vue deleted file mode 100644 index dc9b33847..000000000 --- a/src/views/setup/Install.vue +++ /dev/null @@ -1,213 +0,0 @@ - - - - - diff --git a/src/views/setup/Status.vue b/src/views/setup/Status.vue deleted file mode 100644 index fe6ef7b69..000000000 --- a/src/views/setup/Status.vue +++ /dev/null @@ -1,168 +0,0 @@ - - - - - diff --git a/src/views/setup/ToolsCustom.vue b/src/views/setup/ToolsCustom.vue deleted file mode 100644 index 34b4b7d5c..000000000 --- a/src/views/setup/ToolsCustom.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/src/views/setup/Welcome.vue b/src/views/setup/Welcome.vue deleted file mode 100644 index 5d3b79587..000000000 --- a/src/views/setup/Welcome.vue +++ /dev/null @@ -1,286 +0,0 @@ - - - - - diff --git a/src/views/setup/components/DownloadStatus.vue b/src/views/setup/components/DownloadStatus.vue deleted file mode 100644 index 25f02a174..000000000 --- a/src/views/setup/components/DownloadStatus.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - diff --git a/src/views/setup/components/folderOpen.vue b/src/views/setup/components/folderOpen.vue deleted file mode 100644 index c0198633c..000000000 --- a/src/views/setup/components/folderOpen.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/src/views/setup/components/logo.vue b/src/views/setup/components/logo.vue deleted file mode 100644 index 875c24cf0..000000000 --- a/src/views/setup/components/logo.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - diff --git a/src/views/setup/components/selectEspIdf.vue b/src/views/setup/components/selectEspIdf.vue deleted file mode 100644 index 1ed3e9c74..000000000 --- a/src/views/setup/components/selectEspIdf.vue +++ /dev/null @@ -1,222 +0,0 @@ - - - - - diff --git a/src/views/setup/components/selectPyVersion.vue b/src/views/setup/components/selectPyVersion.vue deleted file mode 100644 index ab12e2367..000000000 --- a/src/views/setup/components/selectPyVersion.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - diff --git a/src/views/setup/components/selectSaveScope.vue b/src/views/setup/components/selectSaveScope.vue deleted file mode 100644 index 6e5107d69..000000000 --- a/src/views/setup/components/selectSaveScope.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/src/views/setup/components/statusSection/espIdf.vue b/src/views/setup/components/statusSection/espIdf.vue deleted file mode 100644 index 303328ab4..000000000 --- a/src/views/setup/components/statusSection/espIdf.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/src/views/setup/components/statusSection/espIdfTools.vue b/src/views/setup/components/statusSection/espIdfTools.vue deleted file mode 100644 index 4451fde67..000000000 --- a/src/views/setup/components/statusSection/espIdfTools.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - diff --git a/src/views/setup/components/statusSection/prerequisites.vue b/src/views/setup/components/statusSection/prerequisites.vue deleted file mode 100644 index 6a90abfea..000000000 --- a/src/views/setup/components/statusSection/prerequisites.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/views/setup/components/statusSection/pyPkgs.vue b/src/views/setup/components/statusSection/pyPkgs.vue deleted file mode 100644 index d83ba619e..000000000 --- a/src/views/setup/components/statusSection/pyPkgs.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/src/views/setup/components/toolDownload.vue b/src/views/setup/components/toolDownload.vue deleted file mode 100644 index 917af4b74..000000000 --- a/src/views/setup/components/toolDownload.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - diff --git a/src/views/setup/components/toolManual.vue b/src/views/setup/components/toolManual.vue deleted file mode 100644 index 07317faca..000000000 --- a/src/views/setup/components/toolManual.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - - - diff --git a/src/views/setup/main.ts b/src/views/setup/main.ts deleted file mode 100644 index 3e576d068..000000000 --- a/src/views/setup/main.ts +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Thursday, 31st August 2023 8:11:44 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createApp } from "vue"; -import { createPinia } from "pinia"; -import { useSetupStore } from "./store"; -import ExistingSetup from "./ExistingSetup.vue"; -import App from "./App.vue"; -import ToolsCustom from "./ToolsCustom.vue"; -import Home from "./Home.vue"; -import Install from "./Install.vue"; -import Status from "./Status.vue"; -import { createRouter, createWebHashHistory } from "vue-router"; - -const routes = [ - { path: "/", component: Home }, - { path: "/existingsetup", component: ExistingSetup }, - { path: "/autoinstall", component: Install }, - { path: "/custom", component: ToolsCustom }, - { path: "/status", component: Status }, -]; - -export const router = createRouter({ - history: createWebHashHistory(), - routes, -}); - -const app = createApp(App); -const pinia = createPinia(); -app.use(pinia); -app.use(router); -app.mount("#app"); - -router.beforeEach((to, from, next) => { - if (from.path === "/autoinstall" && to.path !== "/autoinstall") { - store.setIdfPathError(""); - } - next(); -}); - -const store = useSetupStore(); - -window.addEventListener("message", (event) => { - const msg = event.data; - switch (msg.command) { - case "goToCustomPage": - if (msg.page) { - router.push(msg.page); - } - if (typeof msg.installing !== "undefined") { - store.isIdfInstalling = msg.installing; - } - break; - case "initialLoad": - if (msg.espToolsPath) { - store.toolsFolder = msg.espToolsPath; - } - if (msg.idfVersions) { - store.setEspIdfVersionList(msg.idfVersions); - } - if (msg.idfTags) { - store.espIdfTags = msg.idfTags; - } - if (msg.idfSetups) { - store.idfSetups = msg.idfSetups; - } - if (msg.pyVersionList) { - store.setPyVersionsList(msg.pyVersionList); - } - if (msg.gitVersion) { - store.gitVersion = msg.gitVersion; - } - if (msg.espIdf) { - store.espIdf = msg.espIdf; - } - if (msg.espIdfContainer) { - store.espIdfContainer = msg.espIdfContainer; - } - if (msg.hasPrerequisites) { - store.hasPrerequisites = msg.hasPrerequisites; - } - if (msg.pathSep) { - store.pathSep = msg.pathSep; - } - if (msg.platform) { - store.platform = msg.platform; - } - if (msg.extensionVersion) { - store.extensionVersion = msg.extensionVersion; - } - if (typeof msg.saveScope !== "undefined") { - store.saveScope = msg.saveScope; - } - if (typeof msg.downloadMirror !== "undefined") { - store.setSelectedDownloadMirror(msg.downloadMirror); - } - break; - case "setEspIdfErrorStatus": - if (typeof msg.errorMsg !== "undefined") { - store.espIdfErrorStatus = msg.errorMsg; - store.isIdfInstalling = false; - } - break; - case "setIsIdfInstalling": - if (typeof msg.installing !== "undefined") { - store.isIdfInstalling = msg.installing; - } - break; - case "setIsInstalled": - if (typeof msg.isInstalled !== "undefined") { - store.isIdfInstalled = msg.isInstalled; - if (msg.installed) { - router.push("/welcome"); - } - } - break; - case "setOpenOcdRulesPath": - if (msg.openOCDRulesPath) { - store.openOCDRulesPath = msg.openOCDRulesPath; - } - break; - case "setPyExecErrorStatus": - if (msg.errorMsg) { - store.isIdfInstalling = false; - store.pyExecErrorStatus = msg.errorMsg; - } - break; - case "setRequiredToolsInfo": - if (msg.toolsInfo) { - store.toolsResults = msg.toolsInfo; - } - break; - case "setSetupMode": - if (typeof msg.setupMode !== "undefined") { - store.setupMode = msg.setupMode; - } - break; - case "updateEspIdfFolder": - if (msg.selectedFolder) { - store.espIdf = msg.selectedFolder; - } - break; - case "updateEspIdfContainerFolder": - if (msg.selectedContainerFolder) { - store.espIdfContainer = msg.selectedContainerFolder; - } - break; - case "updateEspIdfStatus": - if (typeof msg.status !== "undefined") { - store.setStatusEspIdf(msg.status); - } - break; - case "updateEspIdfToolsFolder": - if (msg.selectedToolsFolder) { - store.toolsFolder = msg.selectedToolsFolder; - } - break; - case "updateEspIdfToolsStatus": - if (msg.status) { - store.setStatusEspIdfTools(msg.status); - } - break; - case "updateIdfDownloadStatusDetail": - if (msg.detail) { - store.idfDownloadStatus.progressDetail = msg.detail; - } - if (msg.id) { - store.idfDownloadStatus.id = msg.id; - } - break; - case "updateIdfDownloadStatusPercentage": - if (msg.percentage) { - store.idfDownloadStatus.progress = msg.percentage; - } - if (msg.id) { - store.idfDownloadStatus.id = msg.id; - } - break; - case "updatePkgChecksumResult": - if (msg.id && msg.hashResult) { - store.setToolChecksum({ - name: msg.id, - checksum: msg.hashResult, - }); - } - break; - case "updatePkgDownloadDetail": - if (msg.id && msg.progressDetail) { - store.setToolDetail({ - name: msg.id, - detail: msg.progressDetail, - }); - } - break; - case "updatePkgDownloadFailed": - if (msg.id && msg.hasFailed) { - store.setToolFailed({ - name: msg.id, - hasFailed: msg.hasFailed, - }); - } - break; - case "updatePkgDownloadPercentage": - if (msg.id && msg.percentage) { - store.setToolPercentage({ - name: msg.id, - percentage: msg.percentage, - }); - } - break; - case "updatePyReqsLog": - if (msg.pyReqsLog) { - store.pyReqsLog = msg.pyReqsLog; - store.moveToPySection(); - } - break; - case "updatePythonPath": - if (msg.selectedPyPath) { - store.manualPythonPath = msg.selectedPyPath; - } - break; - case "updatePyVEnvStatus": - if (msg.status) { - store.statusPyVEnv = msg.status; - } - break; - case "updateIdfGitDownloadPercentage": - if (msg.id && msg.percentage) { - store.idfGitDownloadStatus.id = msg.id; - store.idfGitDownloadStatus.progress = msg.percentage; - } - break; - case "updateIdfGitDownloadDetail": - if (msg.id && msg.detail) { - store.idfGitDownloadStatus.progressDetail = msg.detail; - } - break; - case "updateIdfPythonDownloadPercentage": - if (msg.id && msg.percentage) { - store.setIdfPythonPercentage({ - name: msg.id, - percentage: msg.percentage, - }); - } - break; - case "updateIdfPythonDownloadDetail": - if (msg.id && msg.detail) { - store.idfPythonDownloadStatus.progressDetail = msg.detail; - } - break; - case "updateIdfGitStatus": - if (msg.status) { - store.setStatusIdfGit(msg.status); - } - break; - case "updateIdfPythonStatus": - if (msg.status) { - store.setStatusIdfPython(msg.status); - } - break; - case "canAccessFileResponse": - if (!msg.exists) { - store.setIdfPathError( - `The path for ESP-IDF is not valid: ${msg.path} not found.` - ); - } else if (msg.noWhiteSpaceSupport && msg.hasWhitespace) { - store.setIdfPathError( - "White spaces are only supported for ESP-IDF path for versions >= 5.0." - ); - } else { - store.setIdfPathError(""); - } - break; - default: - break; - } -}); diff --git a/src/views/setup/store.ts b/src/views/setup/store.ts deleted file mode 100644 index 120398d39..000000000 --- a/src/views/setup/store.ts +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Project: ESP-IDF VSCode Extension - * File Created: Thursday, 31st August 2023 8:12:24 pm - * Copyright 2023 Espressif Systems (Shanghai) CO LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { defineStore } from "pinia"; -import { ref, Ref } from "vue"; -import { - IdfMirror, - IdfSetup, - IDownload, - IEspIdfLink, - IEspIdfTool, - SetupMode, - StatusType, -} from "./types"; - -declare var acquireVsCodeApi: any; -let vscode: any; -try { - vscode = acquireVsCodeApi(); -} catch (error) { - // tslint:disable-next-line: no-console - console.error(error); -} - -export const useSetupStore = defineStore("setup", () => { - let espIdfVersion: Ref = ref(""); - let areToolsValid: Ref = ref(false); - let espIdf: Ref = ref(""); - let espIdfContainer: Ref = ref(""); - let espIdfErrorStatus: Ref = ref(""); - let espIdfVersionList: Ref = ref([]); - let espIdfTags: Ref = ref([]); - let exportedToolsPaths: Ref = ref(""); - let exportedVars: Ref<{ [key: string]: string }> = ref({}); - let gitVersion: Ref = ref(""); - let hasPrerequisites: Ref = ref(false); - let idfDownloadStatus: Ref = ref({ - id: "", - progress: "", - progressDetail: "", - }); - let idfGitDownloadStatus: Ref = ref({ - id: "", - progress: "", - progressDetail: "", - }); - let idfPythonDownloadStatus: Ref = ref({ - id: "", - progress: "", - progressDetail: "", - }); - let idfSetups: Ref = ref([]); - let isEspIdfValid: Ref = ref(false); - let isIdfInstalling: Ref = ref(false); - let isIdfInstalled: Ref = ref(false); - let manualPythonPath: Ref = ref(""); - let openOCDRulesPath: Ref = ref(""); - let pathSep: Ref = ref("/"); - let platform: Ref = ref(""); - let pyExecErrorStatus: Ref = ref(""); - let whiteSpaceErrorIDF: Ref = ref(""); - let whiteSpaceErrorTools: Ref = ref(""); - let whiteSpaceErrorIDFContainer: Ref = ref(""); - let pyReqsLog: Ref = ref(""); - let pyVersionsList: Ref = ref([]); - let saveScope: Ref = ref(1); - let selectedEspIdfVersion: Ref = ref({ - filename: "", - mirror: "", - name: "", - url: "", - version: "", - }); - let selectedIdfMirror: Ref = ref(IdfMirror.Github); - let selectedSysPython: Ref = ref(""); - let setupMode: Ref = ref(SetupMode.express); - let showIdfTagList: Ref = ref(false); - let statusEspIdf: Ref = ref(StatusType.started); - let statusEspIdfTools: Ref = ref(StatusType.pending); - let statusIdfGit: Ref = ref(StatusType.pending); - let statusIdfPython: Ref = ref(StatusType.pending); - let statusPyVEnv: Ref = ref(StatusType.pending); - let toolsFolder: Ref = ref(""); - let toolsResults: Ref = ref([]); - let extensionVersion: Ref = ref(""); - let idfPathError: Ref = ref(""); - let isInstallButtonDisabled: Ref = ref(false); - let pypiIndex: Ref = ref(""); - - function clearIdfPathError() { - idfPathError.value = ""; - isInstallButtonDisabled.value = false; - } - - function setIdfPathError(error: string) { - idfPathError.value = error; - isInstallButtonDisabled.value = !!error; - } - - function validateEspIdfPath(path: string) { - clearIdfPathError(); - vscode.postMessage({ - command: "canAccessFile", - path, - currentVersion: espIdfVersion.value, - }); - } - - function openEspIdfFolder(): Promise { - return new Promise((resolve) => { - vscode.postMessage({ - command: "openEspIdfFolder", - }); - window.addEventListener("message", function handler(event) { - if (event.data.command === "updateEspIdfFolder") { - window.removeEventListener("message", handler); - resolve(event.data.selectedFolder); - } - }); - }); - } - - function openEspIdfContainerFolder(): Promise { - return new Promise((resolve) => { - vscode.postMessage({ - command: "openEspIdfContainerFolder", - }); - window.addEventListener("message", function handler(event) { - if (event.data.command === "updateEspIdfContainerFolder") { - window.removeEventListener("message", handler); - resolve(event.data.selectedContainerFolder); - } - }); - }); - } - - function openEspIdfToolsFolder(): Promise { - return new Promise((resolve) => { - vscode.postMessage({ - command: "openEspIdfToolsFolder", - }); - window.addEventListener("message", function handler(event) { - if (event.data.command === "updateEspIdfToolsFolder") { - window.removeEventListener("message", handler); - resolve(event.data.selectedToolsFolder); - } - }); - }); - } - - window.addEventListener("message", (event) => { - const message = event.data; - if (message.command === "canAccessFileResponse") { - if (!message.exists) { - setIdfPathError( - `The path for ESP-IDF is not valid: ${message.path} not found.` - ); - } else { - setIdfPathError(""); - } - } - }); - - function checkEspIdfTools() { - const pyPath = - selectedSysPython.value === pyVersionsList.value[pyVersionsList.value.length - 1] - ? manualPythonPath.value - : selectedSysPython.value; - console.log({ - command: "checkEspIdfTools", - espIdf: espIdf.value, - pyPath, - toolsPath: JSON.stringify(toolsResults.value), - }); - vscode.postMessage({ - command: "checkEspIdfTools", - espIdf: espIdf.value, - pyPath, - toolsPath: JSON.stringify(toolsResults.value), - }); - } - - function installEspIdf() { - const pyPath = - selectedSysPython.value === - pyVersionsList.value[pyVersionsList.value.length - 1] - ? manualPythonPath.value - : selectedSysPython.value; - vscode.postMessage({ - command: "installEspIdf", - espIdfContainer: espIdfContainer.value, - manualEspIdfPath: espIdf.value, - mirror: selectedIdfMirror.value, - selectedEspIdfVersion: JSON.stringify(selectedEspIdfVersion.value), - selectedPyPath: pyPath, - setupMode: setupMode.value, - toolsPath: toolsFolder.value, - saveScope: saveScope.value, - pypiIndex: pypiIndex.value, - }); - } - - function installEspIdfTools() { - const pyPath = - selectedSysPython.value === - pyVersionsList.value[pyVersionsList.value.length - 1] - ? manualPythonPath.value - : selectedSysPython.value; - vscode.postMessage({ - command: "installEspIdfTools", - espIdf: espIdf.value, - mirror: selectedIdfMirror.value, - pyPath, - toolsPath: toolsFolder.value, - saveScope: saveScope.value, - pypiIndex: pypiIndex.value, - }); - } - - function openPythonPath() { - vscode.postMessage({ - command: "openPythonPath", - }); - } - - function requestInitialValues() { - vscode.postMessage({ - command: "requestInitialValues", - }); - } - - function saveCustomSettings() { - const pyPath = - selectedSysPython.value === - pyVersionsList.value[pyVersionsList.value.length - 1] - ? manualPythonPath.value - : selectedSysPython.value; - vscode.postMessage({ - command: "saveCustomSettings", - espIdfPath: espIdf.value, - pyBinPath: pyPath, - tools: JSON.stringify(toolsResults.value), - toolsPath: toolsFolder.value, - saveScope: saveScope.value, - mirror: selectedIdfMirror.value, - pypiIndex: pypiIndex.value, - }); - } - - function useDefaultSettings() { - vscode.postMessage({ - command: "usePreviousSettings", - }); - } - - function useIdfSetup(payload: number) { - vscode.postMessage({ - command: "useIdfSetup", - selectedIdfSetup: payload, - saveScope: saveScope.value, - }); - } - - function cleanIdfSetups() { - idfSetups.value = []; - vscode.postMessage({ - command: "cleanIdfSetups", - }); - } - - function exploreComponents() { - vscode.postMessage({ - command: "exploreComponents", - }); - } - - function openImportProject() { - vscode.postMessage({ - command: "importProject", - }); - } - - function openNewProjectPanel() { - vscode.postMessage({ - command: "newProject", - }); - } - function requestInitValues() { - vscode.postMessage({ - command: "requestInitialValues", - }); - } - - function setEspIdfVersionList(espIdfVerList: IEspIdfLink[]) { - espIdfVersionList.value = espIdfVerList; - if (espIdfVerList && espIdfVerList.length > 0) { - selectedEspIdfVersion.value = espIdfVerList[0]; - } - } - - function setPyVersionsList(pyVerList: string[]) { - pyVersionsList.value = pyVerList; - if (pyVerList && pyVerList.length > 0) { - selectedSysPython.value = pyVerList[0]; - } - } - - function setSelectedEspIdfVersion(selectedEspIdfVer: IEspIdfLink) { - selectedEspIdfVersion.value = selectedEspIdfVer; - espIdfVersion.value = selectedEspIdfVer.version; - idfDownloadStatus.value.id = selectedEspIdfVer.name; - // Trigger validation whenever the version changes - if (espIdf.value) { - validateEspIdfPath(espIdf.value); - } - } - - function setToolChecksum(toolData: { name: string; checksum: boolean }) { - for (let i = 0; i < toolsResults.value.length; i++) { - if (toolsResults.value[i].name === toolData.name) { - toolsResults.value[i].hashResult = toolData.checksum; - break; - } - } - } - - function setToolDetail(toolData: { name: string; detail: string }) { - for (let i = 0; i < toolsResults.value.length; i++) { - if (toolsResults.value[i].name === toolData.name) { - toolsResults.value[i].progressDetail = toolData.detail; - break; - } - } - } - - function setToolFailed(toolData: { name: string; hasFailed: boolean }) { - for (let i = 0; i < toolsResults.value.length; i++) { - if (toolsResults.value[i].name === toolData.name) { - toolsResults.value[i].hasFailed = toolData.hasFailed; - break; - } - } - } - - function setToolPercentage(toolData: { name: string; percentage: string }) { - for (let i = 0; i < toolsResults.value.length; i++) { - if (toolsResults.value[i].name === toolData.name) { - toolsResults.value[i].progress = toolData.percentage; - break; - } - } - } - - function setStatusEspIdf(status: StatusType) { - statusEspIdf.value = status; - if (status === StatusType.installed) { - idfDownloadStatus.value.progress = "100.00%"; - } - } - - function setStatusEspIdfTools(status: StatusType) { - statusEspIdfTools.value = status; - if (status === StatusType.installed) { - for (let i = 0; i < toolsResults.value.length; i++) { - toolsResults.value[i].progress = "100.00%"; - } - } - moveToPySection(); - } - - function setSelectedDownloadMirror(mirror: IdfMirror) { - selectedIdfMirror.value = - mirror === IdfMirror.Espressif ? IdfMirror.Espressif : IdfMirror.Github; - } - - function moveToPySection() { - let content = document.getElementById("espidftools") as HTMLDivElement; - if (content) { - content.style.display = "none"; - } - const secNew = document.querySelector("#py-install-status") as HTMLElement; - const configList = document.querySelector("#scrollable") as HTMLElement; - if (secNew) { - const endPosition = secNew.getBoundingClientRect().bottom; - configList.scrollTo({ left: 0, top: endPosition - 10, behavior: "auto" }); - } - } - - function toggleContent(containerId: string) { - var content = document.getElementById(containerId) as HTMLDivElement; - content.style.display === "flex" - ? (content.style.display = "none") - : (content.style.display = "flex"); - } - - function setStatusIdfGit(status: StatusType) { - statusIdfGit.value = status; - if (status === StatusType.installed) { - idfGitDownloadStatus.value.progress = "100.00%"; - } - } - - function setStatusIdfPython(status: StatusType) { - statusIdfPython.value = status; - if (status === StatusType.installed) { - idfPythonDownloadStatus.value.progress = "100.00%"; - } - } - - function setIdfPythonPercentage(statusData: { - name: string; - percentage: string; - }) { - idfPythonDownloadStatus.value.id = statusData.name; - idfPythonDownloadStatus.value.progress = statusData.percentage; - } - - return { - areToolsValid, - espIdf, - espIdfContainer, - espIdfErrorStatus, - espIdfVersionList, - espIdfTags, - exportedToolsPaths, - exportedVars, - extensionVersion, - gitVersion, - hasPrerequisites, - idfDownloadStatus, - idfGitDownloadStatus, - idfPythonDownloadStatus, - idfSetups, - isEspIdfValid, - isIdfInstalling, - isIdfInstalled, - manualPythonPath, - moveToPySection, - openOCDRulesPath, - pathSep, - platform, - pyExecErrorStatus, - pyReqsLog, - pyVersionsList, - saveScope, - selectedEspIdfVersion, - selectedIdfMirror, - selectedSysPython, - setupMode, - showIdfTagList, - statusIdfGit, - statusIdfPython, - statusEspIdf, - statusEspIdfTools, - statusPyVEnv, - toolsFolder, - toolsResults, - checkEspIdfTools, - installEspIdf, - installEspIdfTools, - openEspIdfFolder, - openEspIdfContainerFolder, - openEspIdfToolsFolder, - openPythonPath, - requestInitialValues, - saveCustomSettings, - useDefaultSettings, - useIdfSetup, - cleanIdfSetups, - setEspIdfVersionList, - setPyVersionsList, - setSelectedEspIdfVersion, - setToolChecksum, - setToolDetail, - setToolFailed, - setToolPercentage, - setStatusEspIdf, - setStatusEspIdfTools, - setStatusIdfGit, - setStatusIdfPython, - setIdfPythonPercentage, - setSelectedDownloadMirror, - exploreComponents, - openImportProject, - openNewProjectPanel, - requestInitValues, - toggleContent, - whiteSpaceErrorIDF, - whiteSpaceErrorTools, - whiteSpaceErrorIDFContainer, - idfPathError, - isInstallButtonDisabled, - setIdfPathError, - validateEspIdfPath, - clearIdfPathError, - espIdfVersion, - pypiIndex, - }; -}); diff --git a/src/views/size/App.vue b/src/views/size/App.vue index cd05ca253..685b41bec 100644 --- a/src/views/size/App.vue +++ b/src/views/size/App.vue @@ -120,7 +120,7 @@ onMounted(() => { diff --git a/src/views/tracing/components/LeakList.vue b/src/views/tracing/components/LeakList.vue index df4d3d1b7..6f5c35ebe 100644 --- a/src/views/tracing/components/LeakList.vue +++ b/src/views/tracing/components/LeakList.vue @@ -145,5 +145,5 @@ function fetchFunctionFilePath(addr: string): string { diff --git a/src/views/welcome/App.vue b/src/views/welcome/App.vue index fd0a107ea..b2ecfecc3 100644 --- a/src/views/welcome/App.vue +++ b/src/views/welcome/App.vue @@ -5,7 +5,6 @@ import { useWelcomeStore } from "./store"; import Logo from "./components/logo.vue"; import BlogArticles from "./components/BlogArticles.vue"; import { - IconBeaker, IconComment, IconFolderOpened, IconGear, @@ -16,7 +15,7 @@ import { const store = useWelcomeStore(); -const { espIdf, extensionVersion, showOnInit } = storeToRefs(store); +const { extensionVersion } = storeToRefs(store); const whatsNewLink = computed(() => { return `https://github.com/espressif/vscode-esp-idf-extension/releases/tag/v${extensionVersion.value}`; @@ -44,7 +43,7 @@ onMounted(() => { Version: {{ extensionVersion }} See what's new - +