diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 652f1eb77..cc08fc1f4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -23,10 +23,6 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Espressif-IDE Product Information:** -Espressif > Product Information (Copy content from the console and attach as a file) - -**Eclipse Error log:** -Window > Show View > Other > Search for "Error Log" (Attach as a file) - -Please attach the error log as described here https://github.com/espressif/idf-eclipse-plugin#error-log +**Bug Report** +Please attach the bug report zip file generated. The file is generated in the workspace root directory. +Espressif > Generate Bug Report \ No newline at end of file diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml new file mode 100644 index 000000000..8d4c23a07 --- /dev/null +++ b/.github/workflows/bump-version.yml @@ -0,0 +1,127 @@ +name: Bump Version + +on: + workflow_dispatch: + inputs: + new_version: + description: 'New version to bump to (e.g., 3.7.0)' + required: true + type: string + create_pr: + description: 'Create a pull request with the changes' + required: false + type: boolean + default: true + +jobs: + bump-version: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Setup Git + run: | + git config --local user.email "kondal.kolipaka@espressif.com" + git config --local user.name "Kondal Kolipaka" + + - name: Validate and get current version + id: version + run: | + if [[ ! "${{ github.event.inputs.new_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format X.Y.Z (e.g., 3.7.0)" + exit 1 + fi + + CURRENT_VERSION=$(grep -o 'espressif-ide-release-version>[^<]*' releng/com.espressif.idf.configuration/pom.xml | cut -d'>' -f2) + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + echo "new=${{ github.event.inputs.new_version }}" >> $GITHUB_OUTPUT + echo "Current: $CURRENT_VERSION → New: ${{ github.event.inputs.new_version }}" + + - name: Update version in files + run: | + CURRENT_VERSION="${{ steps.version.outputs.current }}" + NEW_VERSION="${{ steps.version.outputs.new }}" + + sed -i "s/Bundle-Version: ${CURRENT_VERSION}\.qualifier/Bundle-Version: ${NEW_VERSION}.qualifier/g" bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF + sed -i "s/version=\"${CURRENT_VERSION}\.qualifier\"/version=\"${NEW_VERSION}.qualifier\"/g" features/com.espressif.idf.feature/feature.xml + sed -i "s/${CURRENT_VERSION}<\/espressif-ide-release-version>/${NEW_VERSION}<\/espressif-ide-release-version>/g" releng/com.espressif.idf.configuration/pom.xml + sed -i "s/version=\"${CURRENT_VERSION}\"/version=\"${NEW_VERSION}\"/g" releng/com.espressif.idf.product/idf.product + + - name: Verify changes + run: | + NEW_VERSION="${{ steps.version.outputs.new }}" + + for file in "bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF:Bundle-Version: ${NEW_VERSION}.qualifier" \ + "features/com.espressif.idf.feature/feature.xml:version=\"${NEW_VERSION}.qualifier\"" \ + "releng/com.espressif.idf.configuration/pom.xml:${NEW_VERSION}" \ + "releng/com.espressif.idf.product/idf.product:version=\"${NEW_VERSION}\""; do + IFS=':' read -r filepath pattern <<< "$file" + if ! grep -q "$pattern" "$filepath"; then + echo "✗ Failed to update $filepath" + exit 1 + fi + echo "✓ Updated $filepath" + done + + - name: Create Pull Request + if: github.event.inputs.create_pr == 'true' + run: | + NEW_VERSION="${{ steps.version.outputs.new }}" + BRANCH_NAME="bump-version-${NEW_VERSION}" + + git checkout -b "$BRANCH_NAME" + git add bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF features/com.espressif.idf.feature/feature.xml releng/com.espressif.idf.configuration/pom.xml releng/com.espressif.idf.product/idf.product + git commit -m "chore: bump version to v${NEW_VERSION} + + - Updated Bundle-Version in MANIFEST.MF + - Updated version in feature.xml + - Updated espressif-ide-release-version in pom.xml + - Updated version in idf.product + + Automated by GitHub Actions workflow" + + git push origin "$BRANCH_NAME" + + gh pr create \ + --title "chore: bump version to v${NEW_VERSION}" \ + --body "This PR automatically bumps the version to v${NEW_VERSION}. + + **Files updated:** + - \`bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF\` + - \`features/com.espressif.idf.feature/feature.xml\` + - \`releng/com.espressif.idf.configuration/pom.xml\` + - \`releng/com.espressif.idf.product/idf.product\` + + **Changes:** + - Bundle-Version: \`${NEW_VERSION}.qualifier\` + - Feature version: \`${NEW_VERSION}.qualifier\` + - Release version: \`${NEW_VERSION}\` + - Product version: \`${NEW_VERSION}\` + + This PR was created automatically by the GitHub Actions workflow." \ + --head "$BRANCH_NAME" \ + --base "master" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Commit directly to master + if: github.event.inputs.create_pr == 'false' + run: | + NEW_VERSION="${{ steps.version.outputs.new }}" + + git add bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF features/com.espressif.idf.feature/feature.xml releng/com.espressif.idf.configuration/pom.xml releng/com.espressif.idf.product/idf.product + git commit -m "chore: bump version to v${NEW_VERSION} + + - Updated Bundle-Version in MANIFEST.MF + - Updated version in feature.xml + - Updated espressif-ide-release-version in pom.xml + - Updated version in idf.product + + Automated by GitHub Actions workflow" + + git push origin master diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12641fe44..9eb43b58d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: repository: espressif/esp-idf path: dependencies/idf-tools submodules: 'true' - ref: release/v5.1 + ref: release/v5.4 - name: Set up Python uses: actions/setup-python@v2 diff --git a/.github/workflows/ci_beta.yml b/.github/workflows/ci_beta.yml deleted file mode 100644 index bf30d5aff..000000000 --- a/.github/workflows/ci_beta.yml +++ /dev/null @@ -1,165 +0,0 @@ -# This workflow will build a Java project with Maven -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven - -name: Beta builds - -on: - push: - tags: - - "v[0-9]+\\.[0-9]+\\.[0-9]+-beta[0-9]*" - -jobs: - build: - - runs-on: macos-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - - name: Build with Maven - run: | - export JARSIGNER_KEYSTORE_B64=${{secrets.JARSIGNER_REL_KEYSTORE_B64}} - export JARSIGNER_STOREPASS=${{secrets.JARSIGNER_REL_STOREPASS}} - export JARSIGNER_ALIAS=${{secrets.JARSIGNER_REL_ALIAS}} - KEYSTORE_FILE="${PWD}/{{secrets.JARSIGNER_KEYSTORE}}" - echo "${KEYSTORE_FILE}" - printf "%s" "${JARSIGNER_KEYSTORE_B64}" | base64 -d > "${KEYSTORE_FILE}" - mvn -e -X clean install -Djarsigner.keystore="${KEYSTORE_FILE}" -Djarsigner.alias="${JARSIGNER_ALIAS}" -Djarsigner.storepass="${JARSIGNER_STOREPASS}" -DskipTests=true - rm -v "${KEYSTORE_FILE}" - - - name: Codesign Espressif-IDE - env: - MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} - MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} - run: | - echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 - /usr/bin/security create-keychain -p espressif build.keychain - /usr/bin/security default-keychain -s build.keychain - /usr/bin/security unlock-keychain -p espressif build.keychain - /usr/bin/security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign - /usr/bin/security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k espressif build.keychain - - echo "codesigning espressif-ide-macosx.cocoa.x86_64" - /usr/bin/codesign --entitlements $PWD/releng/com.espressif.idf.product/entitlements/espressif-ide.entitlement --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/x86_64/Espressif-IDE.app -v - /usr/bin/codesign -v -vvv --deep $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/x86_64/Espressif-IDE.app - - echo "codesigning espressif-ide-macosx.cocoa.aarch64" - /usr/bin/codesign --entitlements $PWD/releng/com.espressif.idf.product/entitlements/espressif-ide.entitlement --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/aarch64/Espressif-IDE.app -v - /usr/bin/codesign -v -vvv --deep $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/aarch64/Espressif-IDE.app - - echo "Creating dmg for espressif-ide-macosx.cocoa.x86_64" - $PWD/releng/ide-dmg-builder/ide-dmg-builder.sh - /usr/bin/codesign --entitlements $PWD/releng/com.espressif.idf.product/entitlements/espressif-ide.entitlement --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg -v - /usr/bin/codesign -v -vvv --deep $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg - - echo "Creating dmg for espressif-ide-macosx.cocoa.aarch64" - $PWD/releng/ide-dmg-builder/ide-dmg-builder-aarch64.sh - /usr/bin/codesign --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg -v - /usr/bin/codesign -v -vvv --deep $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg - - - name: Notarization of Espressif-IDE dmg files - env: - NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }} - NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }} - NOTARIZATION_TEAM_ID: ${{ secrets.NOTARIZATION_TEAM_ID }} - run: | - echo "Create notary keychain" - /usr/bin/security create-keychain -p espressif notary.keychain - /usr/bin/security default-keychain -s notary.keychain - /usr/bin/security unlock-keychain -p espressif notary.keychain - - echo "Create keychain profile" - xcrun notarytool store-credentials "ide-notarytool-profile" --apple-id $NOTARIZATION_USERNAME --team-id $NOTARIZATION_TEAM_ID --password $NOTARIZATION_PASSWORD - xcrun notarytool submit $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg --keychain-profile "ide-notarytool-profile" --wait - - echo "Attach staple for x86_64.dmg" - xcrun stapler staple $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg - - echo "Unlock the notary keychain" - /usr/bin/security unlock-keychain -p espressif notary.keychain - - xcrun notarytool submit $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg --keychain-profile "ide-notarytool-profile" --wait - echo "Attach staple for aarch64.dmg" - xcrun stapler staple $PWD/releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg - - - name: Upload Espressif-IDE-macosx-cocoa-x86_64.dmg - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v2 - with: - name: espressif-ide-macosx-cocoa-x86_64 - path: releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg - - - name: Upload Espressif-IDE-macosx-cocoa-aarch64.dmg - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v2 - with: - name: espressif-ide-macosx.cocoa.aarch64 - path: releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg - - - name: Upload build artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v2 - with: - name: com.espressif.idf.update - path: releng/com.espressif.idf.update/target/repository - - - name: Upload windows rcp - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v2 - with: - name: espressif-ide-win32 - path: releng/com.espressif.idf.product/target/products/Espressif-IDE-*-win32.win32.x86_64.zip - - - name: Upload linux rcp - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v2 - with: - name: espressif-ide-linux - path: releng/com.espressif.idf.product/target/products/Espressif-IDE-*-linux.gtk.x86_64.tar.gz - - - - name: Upload build assets to dl.espressif.com - id: upload-release-asset-espressif - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - run: | - ARCHIVE_DIR="/releng/com.espressif.idf.update/target/" - ARCHIVE_NAME="com.espressif.idf.update*.zip" - echo "${ARCHIVE_DIR}" - echo ${PWD}${ARCHIVE_DIR} - ARCHIVE_FILE_NAME="$(find ${PWD}${ARCHIVE_DIR}${ARCHIVE_NAME})" - echo "${ARCHIVE_FILE_NAME}" - ARCHIVE_PREFIX="com.espressif.idf.update-" - ARCHIVE_SUFFIX="-SNAPSHOT.zip"; - tmp=${ARCHIVE_FILE_NAME#*${ARCHIVE_PREFIX}} # remove prefix - ARCHIVE_VERSION=${tmp%${ARCHIVE_SUFFIX}*} # remove suffix - echo "${ARCHIVE_VERSION}" - FOLDER_NAME="v${ARCHIVE_VERSION}-beta" - mkdir "${FOLDER_NAME}" && cd "${FOLDER_NAME}" && unzip -q ${ARCHIVE_FILE_NAME} && cd .. - echo ${PWD} - ARCHIVE_VERSION_NEW="${ARCHIVE_PREFIX}${ARCHIVE_VERSION}.zip" - echo ${ARCHIVE_VERSION_NEW} - mv ${ARCHIVE_FILE_NAME} ${ARCHIVE_VERSION_NEW} - mv releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64.dmg "releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64-${FOLDER_NAME}.dmg" - mv releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64.dmg "releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64-${FOLDER_NAME}.dmg" - aws s3 rm s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/beta --recursive - aws s3 cp --acl=public-read --recursive "./${FOLDER_NAME}/" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/beta - aws s3 cp --acl=public-read "./releng/index.html" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/beta/ - aws s3 cp --acl=public-read --recursive "./${FOLDER_NAME}/" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/${FOLDER_NAME} - aws s3 cp --acl=public-read --recursive --exclude "*" --include "Espressif-IDE-*" ./releng/com.espressif.idf.product/target/products/ s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/ - aws s3 cp --acl=public-read "${ARCHIVE_VERSION_NEW}" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/ - aws s3 cp --acl=public-read "./releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-x86_64-${FOLDER_NAME}.dmg" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/ - aws s3 cp --acl=public-read "./releng/ide-dmg-builder/Espressif-IDE-macosx-cocoa-aarch64-${FOLDER_NAME}.dmg" s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/ - aws cloudfront create-invalidation --distribution-id ${{ secrets.DL_DISTRIBUTION_ID }} --paths "/dl/idf-eclipse-plugin/updates/beta/*" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-win32.win32.x86_64/beta" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-${ARCHIVE_VERSION}-win32.win32.x86_64.zip" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64/beta" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64-v${ARCHIVE_VERSION}.dmg" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64/beta" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64-v${ARCHIVE_VERSION}.dmg" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-linux.gtk.x86_64/beta" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-${ARCHIVE_VERSION}-linux.gtk.x86_64.tar.gz" diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index 0c9ac18fa..1934f94f9 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -4,6 +4,7 @@ on: push: tags: - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+\\.[0-9]+\\.[0-9]+-beta" env: ARCHIVE_PREFIX: com.espressif.idf.update- @@ -54,7 +55,7 @@ jobs: echo "codesigning espressif-ide-macosx.cocoa.x86_64" /usr/bin/codesign --entitlements $PWD/releng/com.espressif.idf.product/entitlements/espressif-ide.entitlement --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/x86_64/Espressif-IDE.app -v /usr/bin/codesign -v -vvv --deep $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/x86_64/Espressif-IDE.app - + echo "codesigning espressif-ide-macosx.cocoa.aarch64" /usr/bin/codesign --entitlements $PWD/releng/com.espressif.idf.product/entitlements/espressif-ide.entitlement --options runtime --force -s "ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD. (QWXF6GB4AV)" $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/aarch64/Espressif-IDE.app -v /usr/bin/codesign -v -vvv --deep $PWD/releng/com.espressif.idf.product/target/products/com.espressif.idf.product/macosx/cocoa/aarch64/Espressif-IDE.app @@ -256,14 +257,23 @@ jobs: - name: Extract version from tag and prepare folder id: get_version run: | - if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then - TAG="${GITHUB_REF##*/}" - VERSION="${TAG#v}" + TAG="${GITHUB_REF##*/}" + VERSION="${TAG#v}" + if [[ "$TAG" == *"-beta"* ]]; then + IS_BETA="true" + UPLOAD_PATH="beta" + REDIRECT_PATH="beta" else - VERSION="testing" + IS_BETA="false" + UPLOAD_PATH="latest" + REDIRECT_PATH="latest" fi echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "IS_BETA=$IS_BETA" >> $GITHUB_ENV + echo "UPLOAD_PATH=$UPLOAD_PATH" >> $GITHUB_ENV + echo "REDIRECT_PATH=$REDIRECT_PATH" >> $GITHUB_ENV echo "FOLDER=v$VERSION" >> $GITHUB_ENV + echo "TAG=$TAG" >> $GITHUB_ENV - name: Rename DMGs with version suffix run: | @@ -289,8 +299,10 @@ jobs: - name: Upload build assets to dl.espressif.com run: | VERSION="${{ env.VERSION }}" + UPLOAD_PATH="${{ env.UPLOAD_PATH }}" + REDIRECT_PATH="${{ env.REDIRECT_PATH }}" - aws s3 rm "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/latest" --recursive + aws s3 rm "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/$UPLOAD_PATH" --recursive # Extract the update site from the versioned zip and upload its contents TMP_UPDATE_DIR="update_site_tmp" @@ -298,9 +310,9 @@ jobs: unzip -q "com.espressif.idf.update-v$VERSION.zip" -d "$TMP_UPDATE_DIR" echo "Contents of $TMP_UPDATE_DIR:" ls -l "$TMP_UPDATE_DIR" - aws s3 cp --acl=public-read --recursive "$TMP_UPDATE_DIR/artifacts/update/" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/latest" + aws s3 cp --acl=public-read --recursive "$TMP_UPDATE_DIR/artifacts/update/" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/$UPLOAD_PATH" aws s3 cp --acl=public-read --recursive "$TMP_UPDATE_DIR/artifacts/update/" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/v$VERSION" - aws s3 cp --acl=public-read ./releng/index.html "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/latest/" + aws s3 cp --acl=public-read ./releng/index.html "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/$UPLOAD_PATH/" aws s3 cp --acl=public-read "com.espressif.idf.update-v$VERSION.zip" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/updates/" aws s3 cp --acl=public-read --recursive artifacts/linux/ "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/" @@ -308,12 +320,12 @@ jobs: aws s3 cp --acl=public-read "artifacts/macos_arm/Espressif-IDE-macosx-cocoa-aarch64-v$VERSION.dmg" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/" aws s3 cp --acl=public-read "Espressif-IDE-$VERSION-win32.win32.x86_64.zip" "s3://${{ secrets.DL_BUCKET }}/dl/idf-eclipse-plugin/ide/" - aws cloudfront create-invalidation --distribution-id ${{ secrets.DL_DISTRIBUTION_ID }} --paths "/dl/idf-eclipse-plugin/updates/latest/*" + aws cloudfront create-invalidation --distribution-id ${{ secrets.DL_DISTRIBUTION_ID }} --paths "/dl/idf-eclipse-plugin/updates/$UPLOAD_PATH/*" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-win32.win32.x86_64/latest" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-$VERSION-win32.win32.x86_64.zip" + aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-win32.win32.x86_64/$REDIRECT_PATH" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-$VERSION-win32.win32.x86_64.zip" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64/latest" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64-v$VERSION.dmg" + aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64/$REDIRECT_PATH" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-x86_64-v$VERSION.dmg" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64/latest" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64-v$VERSION.dmg" + aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64/$REDIRECT_PATH" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-macosx-cocoa-aarch64-v$VERSION.dmg" - aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-linux.gtk.x86_64/latest" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-$VERSION-linux.gtk.x86_64.tar.gz" + aws s3api put-object --acl=public-read --bucket espdldata --key "dl/idf-eclipse-plugin/ide/Espressif-IDE-linux.gtk.x86_64/$REDIRECT_PATH" --website-redirect-location "/dl/idf-eclipse-plugin/ide/Espressif-IDE-$VERSION-linux.gtk.x86_64.tar.gz" diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml index d0c05ed9a..f17702167 100644 --- a/.github/workflows/ci_windows.yml +++ b/.github/workflows/ci_windows.yml @@ -37,10 +37,10 @@ jobs: with: maven-version: 3.9.6 - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Build with Maven diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 3741f50ae..4de30b9a2 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,4 +1,4 @@ -name: Precommit Hook +name: Conventional Commits Check on: pull_request: @@ -10,15 +10,72 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install pre-commit run: pip install pre-commit - - name: Run pre-commit hook - run: pre-commit run --all-files \ No newline at end of file + - name: Validate commit messages + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + COMMITS=$(git log --oneline $BASE_SHA..$HEAD_SHA) + else + COMMITS=$(git log --oneline -1) + fi + + FAILED_COMMITS=() + while IFS= read -r commit_line; do + if [ -n "$commit_line" ]; then + COMMIT_MSG=$(echo "$commit_line" | cut -d' ' -f2-) + COMMIT_HASH=$(echo "$commit_line" | cut -d' ' -f1) + + # Create temporary commit message file for pre-commit validation + echo "$COMMIT_MSG" > /tmp/commit_msg + + # Run the conventional commit linter + if ! pre-commit run conventional-precommit-linter --hook-stage commit-msg --commit-msg-filename /tmp/commit_msg; then + FAILED_COMMITS+=("$commit_line") + fi + + rm -f /tmp/commit_msg + fi + done <<< "$COMMITS" + + if [ ${#FAILED_COMMITS[@]} -gt 0 ]; then + echo "Conventional commit check failed!" + echo "Invalid commits:" + for commit in "${FAILED_COMMITS[@]}"; do + echo " - $commit" + done + echo "" + echo "Required format: type(scope): description" + echo "" + echo "Valid types: feat, fix, docs, style, refactor, perf, test, chore, ci, build, revert" + echo "" + echo "Examples for ESP-IDF Eclipse Plugin:" + echo " feat: add ESP32-S3 target support" + echo " fix: resolve serial monitor connection issue" + echo " docs: update installation guide for macOS" + echo " chore: update ESP-IDF version to 5.3" + echo " feat(debug): add JTAG debugging support" + echo " fix(flash): resolve flashing timeout on Windows" + echo " test: add unit tests for toolchain manager" + echo " ci: update GitHub Actions runners" + echo " build: update Maven dependencies" + echo " refactor(ui): simplify project creation wizard" + echo "" + echo "To fix commits, you can:" + echo " 1. Use 'git commit --amend' for the last commit" + echo " 2. Use 'git rebase -i HEAD~n' to edit multiple commits" + echo " 3. Use 'git reset --soft HEAD~n' and recommit" + exit 1 + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index f2cf228f3..55051b15d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store tests/com.espressif.idf.ui.test/target/classes/default-test.properties +/tests/**/ws/ workspace/.metadata/.log /docs/_build/ /docs/.DS_Store @@ -8,3 +9,4 @@ workspace/.metadata/.log /.github/.DS_Store /docs/_static/.DS_Store + diff --git a/bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF b/bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF index 56f6d8d5a..c5fdb4649 100644 --- a/bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF +++ b/bundles/com.espressif.idf.branding/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: com.espressif.idf.branding;singleton:=true -Bundle-Version: 3.6.0.qualifier +Bundle-Version: 3.7.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/bundles/com.espressif.idf.core/META-INF/MANIFEST.MF b/bundles/com.espressif.idf.core/META-INF/MANIFEST.MF index b714fd244..05973eecf 100644 --- a/bundles/com.espressif.idf.core/META-INF/MANIFEST.MF +++ b/bundles/com.espressif.idf.core/META-INF/MANIFEST.MF @@ -38,6 +38,7 @@ Automatic-Module-Name: com.espressif.idf.core Bundle-ActivationPolicy: lazy Export-Package: com.espressif.idf.core, com.espressif.idf.core.actions, + com.espressif.idf.core.bug, com.espressif.idf.core.build, com.espressif.idf.core.configparser, com.espressif.idf.core.configparser.vo, diff --git a/bundles/com.espressif.idf.core/OSGI-INF/l10n/bundle.properties b/bundles/com.espressif.idf.core/OSGI-INF/l10n/bundle.properties index 892587cea..38d9b1b0c 100644 --- a/bundles/com.espressif.idf.core/OSGI-INF/l10n/bundle.properties +++ b/bundles/com.espressif.idf.core/OSGI-INF/l10n/bundle.properties @@ -7,3 +7,4 @@ openocd_scripts = OPENOCD_SCRIPTS represents the value specified in environment jtag_flash_args = JTAG_FLASH_ARGS dynamically converts ${JTAG_FLASH_ARGS} into a command line with -c and -f options. These options are generated based on the current launch target, configuring settings like flash voltage and specifying configuration files for the JTAG interface and target device. gdb_client_executable = GDB_CLIENT_EXECUTABLE is a dynamic variable that is replaced during the debug session with the appropriate toolchain path automatically based on the selected target. serial_port = serial_port is a dynamic variable that is replaced by the serial port specified in the launch target +flash_command = Specifies which idf.py flash command should be executed, depending on whether flash encryption is enabled (e.g., "flash" or "encrypted-flash"). diff --git a/bundles/com.espressif.idf.core/plugin.xml b/bundles/com.espressif.idf.core/plugin.xml index 015375aec..669973799 100644 --- a/bundles/com.espressif.idf.core/plugin.xml +++ b/bundles/com.espressif.idf.core/plugin.xml @@ -244,72 +244,6 @@ config-only component and an interface library is created instead." name="KCONFIG_PROJBUILD"> - - - - - - - - - - - - - - - - - - + + diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/DefaultBoardProvider.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/DefaultBoardProvider.java index 81f940759..10a8abb9a 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/DefaultBoardProvider.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/DefaultBoardProvider.java @@ -58,6 +58,12 @@ public String getDefaultBoard(String targetName) { List boardsList = this.espConfigParser.getBoardsForTarget(targetName); String[] boards = boardsList.stream().map(Board::name).toArray(String[]::new); + + if (boards.length == 0) + { + return EspTarget.enumOf(targetName).board; + } + return boards[getIndexOfDefaultBoard(targetName, boards)]; } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/IDFConstants.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/IDFConstants.java index abc58e809..63fdb6033 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/IDFConstants.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/IDFConstants.java @@ -39,22 +39,22 @@ public interface IDFConstants * idf python file */ String IDF_PYTHON_SCRIPT = "idf.py"; //$NON-NLS-1$ - + /** * idf.py list targets */ - String IDF_LIST_TARGETS_CMD = "--list-targets"; //$NON-NLS-1$ - + String IDF_LIST_TARGETS_CMD = "--list-targets"; //$NON-NLS-1$ + /** * idf_monitor python file */ String IDF_MONITOR_PYTHON_SCRIPT = "idf_monitor.py"; //$NON-NLS-1$ - + /** * idf sysviewtrace_proc script file */ String IDF_SYSVIEW_TRACE_SCRIPT = "sysviewtrace_proc.py"; //$NON-NLS-1$ - + /** * idf app_trace_folder */ @@ -64,17 +64,17 @@ public interface IDFConstants * idf tools file */ String IDF_TOOLS_SCRIPT = "idf_tools.py"; //$NON-NLS-1$ - + /** * idf_monitor.py */ String IDF_MONITOR_SCRIPT = "idf_monitor.py"; //$NON-NLS-1$ - + /** * idf size file */ String IDF_SIZE_SCRIPT = "idf_size.py"; //$NON-NLS-1$ - + /** * idf tools.json file for installable tools */ @@ -84,9 +84,11 @@ public interface IDFConstants * /tools */ String TOOLS_FOLDER = "tools"; //$NON-NLS-1$ - + String FLASH_CMD = "flash"; //$NON-NLS-1$ + String FLASH_ENCRYPTED_CMD = "encrypted-flash"; //$NON-NLS-1$ + /** * /tools/cmake */ @@ -106,7 +108,7 @@ public interface IDFConstants * Json config menu file name */ String KCONFIG_MENUS_JSON = "kconfig_menus.json"; //$NON-NLS-1$ - + /** * sdk config json file */ @@ -130,9 +132,9 @@ public interface IDFConstants String TOOLS_INSTALL_CMD = "install"; //$NON-NLS-1$ String TOOLS_INSTALL_ALL_CMD = "all"; //$NON-NLS-1$ - + String TOOLS_INSTALL_PYTHON_CMD = "install-python-env"; //$NON-NLS-1$ - + String TOOLS_LIST_CMD = "list"; //$NON-NLS-1$ String TOOLS_EXPORT_FORMAT_KEYVALUE = "key-value"; //$NON-NLS-1$ @@ -140,18 +142,18 @@ public interface IDFConstants String TOOLS_EXPORT_CMD_FORMAT = "--format"; //$NON-NLS-1$ String TOOLS_EXPORT_CMD_FORMAT_VAL = TOOLS_EXPORT_CMD_FORMAT + "=" + TOOLS_EXPORT_FORMAT_KEYVALUE; //$NON-NLS-1$ - + String PYTHON_CMD = "python"; //$NON-NLS-1$ - + String PYTHON3_CMD = "python3"; //$NON-NLS-1$ /** * /components */ String COMPONENTS_FOLDER = "components"; //$NON-NLS-1$ - + String ESP_CORE_DUMP_FOLDER = "espcoredump"; //$NON-NLS-1$ - + String ESP_CORE_DUMP_SCRIPT = "espcoredump.py"; //$NON-NLS-1$ /** @@ -172,13 +174,13 @@ public interface IDFConstants String ESP_TOOL_CHIP_ID_CMD = "chip_id"; //$NON-NLS-1$ String ESP_TOOL_ERASE_FLASH_CMD = "erase_flash"; //$NON-NLS-1$ - + String ESP_WRITE_FLASH_CMD = "write_flash"; //$NON-NLS-1$ /** * Property to store project custom build directory */ String BUILD_DIR_PROPERTY = "idf.buildDirectory.property"; //$NON-NLS-1$ - + String PROECT_DESCRIPTION_JSON = "project_description.json"; //$NON-NLS-1$ } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/BugReportGenerator.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/BugReportGenerator.java new file mode 100644 index 000000000..3663feb5c --- /dev/null +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/BugReportGenerator.java @@ -0,0 +1,456 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. + * All rights reserved. Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.core.bug; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; + +import com.espressif.idf.core.IDFConstants; +import com.espressif.idf.core.IDFCorePlugin; +import com.espressif.idf.core.IDFCorePreferenceConstants; +import com.espressif.idf.core.IDFEnvironmentVariables; +import com.espressif.idf.core.ProcessBuilderFactory; +import com.espressif.idf.core.SystemExecutableFinder; +import com.espressif.idf.core.logging.Logger; +import com.espressif.idf.core.util.FileUtil; +import com.espressif.idf.core.util.IDFUtil; +import com.espressif.idf.core.util.StringUtil; + +/** + * This class generates a bug report zip file containing: 1. Installed tools information 2. Product information 3. Basic + * system information (OS, Arch, Memory) 4. IDE metadata logs 5. eim logs (if available) + * + * The generated zip file is named with a timestamp and stored in the workspace directory. + * + * @author Ali Azam Rana + * + */ +public class BugReportGenerator +{ + private static final String JAVA_RUNTIME_VERSION_MSG = "Java Runtime Version:"; //$NON-NLS-1$ + private static final String OPERATING_SYSTEM_MSG = "Operating System:"; //$NON-NLS-1$ + private static final String ECLIPSE_CDT_MSG = "Eclipse CDT Version:"; //$NON-NLS-1$ + private static final String IDF_ECLIPSE_PLUGIN_VERSION_MSG = "IDF Eclipse Plugin Version:"; //$NON-NLS-1$ + private static final String ECLIPSE_VERSION_MSG = "Eclipse Version:"; //$NON-NLS-1$ + private static final String PYTHON_IDF_ENV_MSG = "Python set for IDF_PYTHON_ENV:"; //$NON-NLS-1$ + private static final String NOT_FOUND_MSG = ""; //$NON-NLS-1$ + + private static final String ECLIPSE_LOG_FILE_NAME = ".log"; //$NON-NLS-1$ + private static final String ECLIPSE_METADATA_DIRECTORY = ".metadata"; //$NON-NLS-1$ + private static final String UNKNOWN = "Unknown"; //$NON-NLS-1$ + private static final String BUG_REPORT_DIRECTORY_PREFIX = "bug_report_"; //$NON-NLS-1$ + private File bugReportDirectory; + + private enum ByteUnit + { + B("B"), //$NON-NLS-1$ + KB("KB"), //$NON-NLS-1$ + MB("MB"), //$NON-NLS-1$ + GB("GB"), //$NON-NLS-1$ + TB("TB"), //$NON-NLS-1$ + PB("PB"); //$NON-NLS-1$ + + final String label; + + ByteUnit(String label) + { + this.label = label; + } + + ByteUnit next() + { + int i = ordinal(); + return i < PB.ordinal() ? values()[i + 1] : PB; + } + } + + public BugReportGenerator() + { + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); //$NON-NLS-1$ + bugReportDirectory = getWorkspaceDirectory().resolve(BUG_REPORT_DIRECTORY_PREFIX + timestamp + File.separator) + .toFile(); + } + + private File getEimLogPath() + { + String eimPath = StringUtil.EMPTY; + switch (Platform.getOS()) + { + case Platform.OS_WIN32: + eimPath = System.getenv("LOCALAPPDATA"); //$NON-NLS-1$ + if (!StringUtil.isEmpty(eimPath)) + { + eimPath = eimPath + "\\eim\\logs"; //$NON-NLS-1$ + } + break; + case Platform.OS_MACOSX: + eimPath = System.getProperty("user.home"); //$NON-NLS-1$ + if (!StringUtil.isEmpty(eimPath)) + { + eimPath = eimPath + "/Library/Application Support/eim/logs"; //$NON-NLS-1$ + } + break; + case Platform.OS_LINUX: + eimPath = System.getProperty("user.home"); //$NON-NLS-1$ + if (!StringUtil.isEmpty(eimPath)) + { + eimPath = eimPath + "/.local/share/eim/logs"; //$NON-NLS-1$ + } + break; + default: + break; + } + + return new File(eimPath); + } + + private Path getWorkspaceDirectory() + { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + File workspaceRoot = workspace.getRoot().getLocation().toFile(); + return workspaceRoot.toPath(); + } + + private List getIdeMetadataLogsFile() + { + File metadataDir = getWorkspaceDirectory().resolve(ECLIPSE_METADATA_DIRECTORY).toFile(); + File[] allFiles = metadataDir.listFiles(); + List logFiles = new LinkedList<>(); + if (!metadataDir.exists() || !metadataDir.isDirectory() || allFiles == null) + { + return logFiles; + } + File activeLog = new File(metadataDir, ECLIPSE_LOG_FILE_NAME); + LocalDate refDate = null; + if (activeLog.exists() && activeLog.isFile()) + { + refDate = Instant.ofEpochMilli(activeLog.lastModified()).atZone(ZoneId.systemDefault()).toLocalDate(); + } + + for (File file : allFiles) + { + if (file.isDirectory()) + { + continue; + } + String fileName = file.getName(); + if (fileName.equals("version.ini")) //$NON-NLS-1$ + { + logFiles.add(file); + continue; + } + if (fileName.endsWith(ECLIPSE_LOG_FILE_NAME)) + { + if (fileName.equals(ECLIPSE_LOG_FILE_NAME)) + { + logFiles.add(file); + continue; + } + + // Including only same day log files or one day earlier to ignore any late night logs. + if (refDate != null) + { + LocalDate fileDate = Instant.ofEpochMilli(file.lastModified()).atZone(ZoneId.systemDefault()) + .toLocalDate(); + + if (fileDate.equals(refDate) || fileDate.equals(refDate.minusDays(1))) + { + logFiles.add(file); + } + } + + } + } + + return logFiles; + } + + private File createBasicSystemInfoFile() throws IOException + { + String osName = System.getProperty("os.name", UNKNOWN); //$NON-NLS-1$ + String osVersion = System.getProperty("os.version", UNKNOWN); //$NON-NLS-1$ + String arch = System.getProperty("os.arch", UNKNOWN); //$NON-NLS-1$ + + long freePhys = -1L; + long totalPhys = -1L; + try + { + com.sun.management.OperatingSystemMXBean osBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory + .getOperatingSystemMXBean(); + freePhys = osBean.getFreeMemorySize(); + totalPhys = osBean.getTotalMemorySize(); + } + catch (Throwable t) + { + // jdk.management module not present or different VM; leave values as -1. + } + + StringBuilder sb = new StringBuilder(); + sb.append("Basic System Info").append(System.lineSeparator()); //$NON-NLS-1$ + sb.append("==================").append(System.lineSeparator()); //$NON-NLS-1$ + sb.append("Generated: ").append(LocalDateTime.now()).append(System.lineSeparator()); //$NON-NLS-1$ + sb.append("OS : ").append(osName).append(" ").append(osVersion).append(System.lineSeparator()); //$NON-NLS-1$ //$NON-NLS-2$ + sb.append("Arch : ").append(arch).append(System.lineSeparator()); //$NON-NLS-1$ + if (totalPhys >= 0 && freePhys >= 0) + { + long used = totalPhys - freePhys; + sb.append("Memory :").append(System.lineSeparator()); //$NON-NLS-1$ + sb.append(" Total : ").append(humanBytes(totalPhys)).append(" (").append(totalPhys) //$NON-NLS-1$ //$NON-NLS-2$ + .append(" bytes)").append(System.lineSeparator()); //$NON-NLS-1$ + sb.append(" Available : ").append(humanBytes(freePhys)).append(" (").append(freePhys).append(" bytes)") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + .append(System.lineSeparator()); + sb.append(" In Use : ").append(humanBytes(used)).append(" (").append(used).append(" bytes)") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + .append(System.lineSeparator()); + } + else + { + sb.append("Memory : Unavailable (OS-level physical memory query unsupported on this JVM)") //$NON-NLS-1$ + .append(System.lineSeparator()); + } + + // Use your existing helper that writes content to a temp file with the given name. + // This will create a file named exactly "basic_system_info". + return FileUtil.createFileWithContentsInDirectory("basic_system_info", sb.toString(), //$NON-NLS-1$ + bugReportDirectory.getAbsolutePath()); + } + + private static String humanBytes(long bytes) + { + double v = (double) bytes; + ByteUnit unit = ByteUnit.B; + while (v >= 1024.0 && unit != ByteUnit.PB) + { + v /= 1024.0; + unit = unit.next(); + } + return String.format(Locale.ROOT, "%.2f %s", v, unit.label); //$NON-NLS-1$ + } + + /** + * Generates a bug report zip and also returns the path of the generated zip file. + * + * @return + */ + public String generateBugReport() + { + if (!bugReportDirectory.exists()) + { + bugReportDirectory.mkdirs(); + } + + try + { + File installedToolsFile = getInstalledToolsInfoFile(); + File productInfoFile = getProductInfoFile(); + File basicSysInfoFile = createBasicSystemInfoFile(); + + List filesToZip = new LinkedList<>(); + filesToZip.add(installedToolsFile); + filesToZip.add(productInfoFile); + filesToZip.add(basicSysInfoFile); + + List metadataLogsFile = getIdeMetadataLogsFile(); + File ideLogDir = new File(bugReportDirectory.getAbsolutePath() + File.separator + "ide_metadata_logs"); //$NON-NLS-1$ ) + if (!ideLogDir.exists()) + { + ideLogDir.mkdirs(); + } + + for (File logFile : metadataLogsFile) + { + FileUtil.copyFile(logFile, new File(ideLogDir.getAbsolutePath() + File.separator + logFile.getName())); + } + File eimLogPath = getEimLogPath(); + Logger.log("EIM log path: " + eimLogPath.getAbsolutePath()); //$NON-NLS-1$ + File eimLogDir = new File(bugReportDirectory.getAbsolutePath() + File.separator + "eim_logs"); //$NON-NLS-1$ ) + IStatus status = FileUtil.copyDirectory(eimLogPath, eimLogDir); + Logger.log("EIM log copy status-code: " + status.getCode()); //$NON-NLS-1$ + Logger.log("EIM log copy status-message: " + status.getMessage()); //$NON-NLS-1$ + + + // Zip the bug report directory + FileUtil.zipDirectory(bugReportDirectory, bugReportDirectory.getAbsolutePath() + ".zip"); //$NON-NLS-1$ + } + catch (IOException e) + { + Logger.log(e); + } + return bugReportDirectory.getAbsolutePath() + ".zip"; //$NON-NLS-1$ + } + + private File getInstalledToolsInfoFile() throws IOException + { + File installedToolsFile = new File(bugReportDirectory, "installed_tools.txt"); //$NON-NLS-1$ + + List arguments = new ArrayList(); + IPath gitPath = new SystemExecutableFinder().find("git"); //$NON-NLS-1$ + Logger.log("GIT path:" + gitPath); //$NON-NLS-1$ + String gitExecutablePath = gitPath.getDevice(); + if (StringUtil.isEmpty(gitExecutablePath)) + { + gitExecutablePath = new IDFEnvironmentVariables().getEnvValue(IDFEnvironmentVariables.GIT_PATH); + } + + arguments.add(IDFUtil.getIDFPythonEnvPath()); + arguments.add(IDFUtil.getIDFToolsScriptFile().getAbsolutePath()); + arguments.add(IDFConstants.TOOLS_LIST_CMD); + Logger.log("Executing command: " + String.join(" ", arguments)); //$NON-NLS-1$ //$NON-NLS-2$ + Map environment = new HashMap<>(IDFUtil.getSystemEnv()); + Logger.log(environment.toString()); + environment.put("PYTHONUNBUFFERED", "1"); //$NON-NLS-1$ //$NON-NLS-2$ + + environment.put("IDF_GITHUB_ASSETS", //$NON-NLS-1$ + Platform.getPreferencesService().getString(IDFCorePlugin.PLUGIN_ID, + IDFCorePreferenceConstants.IDF_GITHUB_ASSETS, + IDFCorePreferenceConstants.IDF_GITHUB_ASSETS_DEFAULT_GLOBAL, null)); + + environment.put("PIP_EXTRA_INDEX_URL", //$NON-NLS-1$ + Platform.getPreferencesService().getString(IDFCorePlugin.PLUGIN_ID, + IDFCorePreferenceConstants.PIP_EXTRA_INDEX_URL, + IDFCorePreferenceConstants.PIP_EXTRA_INDEX_URL_DEFAULT_GLOBAL, null)); + + if (StringUtil.isEmpty(gitExecutablePath)) + { + Logger.log("Git executable path is empty. Please check GIT_PATH environment variable."); //$NON-NLS-1$ + } + else + { + addGitToEnvironment(environment, gitExecutablePath); + } + String output = runCommand(arguments, environment); + Files.write(installedToolsFile.toPath(), output.getBytes(), StandardOpenOption.CREATE_NEW); + return installedToolsFile; + } + + private void addGitToEnvironment(Map environment, String gitExecutablePath) + { + IPath gitPath = new org.eclipse.core.runtime.Path(gitExecutablePath); + if (gitPath.toFile().exists()) + { + String gitDir = gitPath.removeLastSegments(1).toOSString(); + String path1 = environment.get("PATH"); //$NON-NLS-1$ + String path2 = environment.get("Path"); //$NON-NLS-1$ + if (!StringUtil.isEmpty(path1) && !path1.contains(gitDir)) // Git not found on the PATH environment + { + path1 = gitDir.concat(";").concat(path1); //$NON-NLS-1$ + environment.put("PATH", path1); //$NON-NLS-1$ + } + else if (!StringUtil.isEmpty(path2) && !path2.contains(gitDir)) // Git not found on the Path environment + { + path2 = gitDir.concat(";").concat(path2); //$NON-NLS-1$ + environment.put("Path", path2); //$NON-NLS-1$ + } + } + } + + private File getProductInfoFile() throws IOException + { + File productInfoFile = new File(bugReportDirectory, "product_information.txt"); //$NON-NLS-1$ + + String pythonExe = IDFUtil.getIDFPythonEnvPath(); + String idfPath = IDFUtil.getIDFPath(); + if (StringUtil.isEmpty(pythonExe) || StringUtil.isEmpty(idfPath)) + { + Files.write(productInfoFile.toPath(), "IDF_PATH and IDF_PYTHON_ENV_PATH are not found".getBytes(), //$NON-NLS-1$ + StandardOpenOption.CREATE_NEW); + return productInfoFile; + } + + StringBuilder sb = new StringBuilder(); + sb.append(System.lineSeparator()); + sb.append(OPERATING_SYSTEM_MSG + System.getProperty("os.name").toLowerCase()); //$NON-NLS-1$ + sb.append(System.lineSeparator()); + sb.append(JAVA_RUNTIME_VERSION_MSG + + (Optional.ofNullable(System.getProperty("java.runtime.version")).orElse(NOT_FOUND_MSG))); //$NON-NLS-1$ + sb.append(System.lineSeparator()); + sb.append(ECLIPSE_VERSION_MSG + (Optional.ofNullable(Platform.getBundle("org.eclipse.platform")) //$NON-NLS-1$ + .map(o -> o.getVersion().toString()).orElse(NOT_FOUND_MSG))); // $NON-NLS-1$ + sb.append(System.lineSeparator()); + sb.append(ECLIPSE_CDT_MSG + (Optional.ofNullable(Platform.getBundle("org.eclipse.cdt")) //$NON-NLS-1$ + .map(o -> o.getVersion().toString()).orElse(NOT_FOUND_MSG))); // $NON-NLS-1$ + sb.append(System.lineSeparator()); + sb.append( + IDF_ECLIPSE_PLUGIN_VERSION_MSG + (Optional.ofNullable(Platform.getBundle("com.espressif.idf.branding")) //$NON-NLS-1$ + .map(o -> o.getVersion().toString()).orElse(NOT_FOUND_MSG))); // $NON-NLS-1$ + sb.append(System.lineSeparator()); + showEspIdfVersion(); + sb.append(PYTHON_IDF_ENV_MSG + + (Optional.ofNullable(getPythonExeVersion(IDFUtil.getIDFPythonEnvPath())).orElse(NOT_FOUND_MSG))); + sb.append(System.lineSeparator()); + + Files.write(productInfoFile.toPath(), sb.toString().getBytes(), StandardOpenOption.CREATE_NEW); + return productInfoFile; + } + + private void showEspIdfVersion() + { + if (IDFUtil.getIDFPath() != null && IDFUtil.getIDFPythonEnvPath() != null) + { + List commands = new ArrayList<>(); + commands.add(IDFUtil.getIDFPythonEnvPath()); + commands.add(IDFUtil.getIDFPythonScriptFile().getAbsolutePath()); + commands.add("--version"); //$NON-NLS-1$ + Map envMap = new IDFEnvironmentVariables().getSystemEnvMap(); + Logger.log(runCommand(commands, envMap)); + } + else + { + Logger.log("ESP-IDF version cannot be checked. IDF_PATH or IDF_PYTHON_ENV_PATH are not set."); //$NON-NLS-1$ + } + } + + private String runCommand(List arguments, Map env) + { + String exportCmdOp = ""; //$NON-NLS-1$ + ProcessBuilderFactory processRunner = new ProcessBuilderFactory(); + try + { + IStatus status = processRunner.runInBackground(arguments, org.eclipse.core.runtime.Path.ROOT, env); + if (status == null) + { + Logger.log(IDFCorePlugin.getPlugin(), IDFCorePlugin.errorStatus("Status can't be null", null)); //$NON-NLS-1$ + } + + // process export command output + exportCmdOp = status.getMessage(); + Logger.log(exportCmdOp); + } + catch (Exception e1) + { + Logger.log(IDFCorePlugin.getPlugin(), e1); + } + return exportCmdOp; + } + + private String getPythonExeVersion(String pythonExePath) + { + List commands = new ArrayList<>(); + commands.add(pythonExePath); + commands.add("--version"); //$NON-NLS-1$ + return pythonExePath != null ? runCommand(commands, IDFUtil.getSystemEnv()) : null; + } +} \ No newline at end of file diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/GithubIssueOpener.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/GithubIssueOpener.java new file mode 100644 index 000000000..174cc680c --- /dev/null +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/bug/GithubIssueOpener.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. + * All rights reserved. Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.core.bug; + +import java.awt.Desktop; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * Opens the default web browser to create a new issue on the GitHub repository. + * + * @author Ali Azam Rana + */ +public class GithubIssueOpener +{ + private static final String issueUrlBase = "https://github.com/espressif/idf-eclipse-plugin/issues/new"; //$NON-NLS-1$ + + public static void openNewIssue() + throws Exception + { + String q = "&template=" + enc("bug_report.md"); // e.g. "bug_report.md" //$NON-NLS-1$ //$NON-NLS-2$ + URI uri = new URI(issueUrlBase + "?" + q); //$NON-NLS-1$ + Desktop.getDesktop().browse(uri); + } + + private static String enc(String s) throws UnsupportedEncodingException + { + return URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + } +} diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFLaunchConstants.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFLaunchConstants.java index 1ba1da6b1..fbf673fd4 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFLaunchConstants.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFLaunchConstants.java @@ -19,4 +19,6 @@ public final class IDFLaunchConstants public static final String SERIAL_MONITOR_ENCODING = "SERIAL_MONITOR_ENCODING"; //$NON-NLS-1$ public static final String BUILD_FOLDER_PATH = "com.espressif.idf.launch.serial.core.idfBuildFolderPath"; //$NON-NLS-1$ public static final String OPENOCD_USB_LOCATION = "OPENOCD_USB_ADAPTER_LOCATION"; //$NON-NLS-1$ + public static final String FLASH_ENCRYPTION_ENABLED = "com.espressif.idf.launch.FLASH_ENCRYPTION_ENABLED"; //$NON-NLS-1$ + } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/ESPToolChainManager.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/ESPToolChainManager.java index 659677982..c647f6f4d 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/ESPToolChainManager.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/ESPToolChainManager.java @@ -34,10 +34,8 @@ import org.eclipse.cdt.core.build.IToolChainManager; import org.eclipse.cdt.core.build.IToolChainProvider; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.launchbar.core.target.ILaunchTarget; import org.eclipse.launchbar.core.target.ILaunchTargetManager; import org.eclipse.launchbar.core.target.ILaunchTargetWorkingCopy; @@ -74,23 +72,27 @@ public ESPToolChainManager() private static Map readESPToolchainRegistry() { - IConfigurationElement[] configElements = Platform.getExtensionRegistry() - .getConfigurationElementsFor("com.espressif.idf.core.toolchain"); //$NON-NLS-1$ - for (IConfigurationElement iConfigurationElement : configElements) + // Read targets dynamically from ESP-IDF constants.py instead of plugin.xml + String idfPath = IDFUtil.getIDFPath(); + IDFTargets idfTargets = IDFTargetsReader.readTargetsFromEspIdf(idfPath); + + // Convert dynamic targets to toolchain elements + for (IDFTargets.IDFTarget target : idfTargets.getAllTargets()) { - String name = iConfigurationElement.getAttribute("name"); //$NON-NLS-1$ - String id = iConfigurationElement.getAttribute("id"); //$NON-NLS-1$ - String arch = iConfigurationElement.getAttribute("arch"); //$NON-NLS-1$ - String fileName = iConfigurationElement.getAttribute("fileName"); //$NON-NLS-1$ - String compilerPattern = iConfigurationElement.getAttribute("compilerPattern"); //$NON-NLS-1$ - String debuggerPatten = iConfigurationElement.getAttribute("debuggerPattern"); //$NON-NLS-1$ + String name = target.getName(); + String id = target.getToolchainId(); + String arch = target.getArchitecture(); + String fileName = target.getToolchainFileName(); + String compilerPattern = target.getCompilerPattern(); + String debuggerPattern = target.getDebuggerPattern(); String uniqueToolChainId = name.concat("/").concat(arch).concat("/").concat(fileName); //$NON-NLS-1$ //$NON-NLS-2$ toolchainElements.put(uniqueToolChainId, - new ESPToolChainElement(name, id, arch, fileName, compilerPattern, debuggerPatten)); - + new ESPToolChainElement(name, id, arch, fileName, compilerPattern, debuggerPattern)); } + + Logger.log("Dynamically loaded " + toolchainElements.size() + " toolchain elements from ESP-IDF"); //$NON-NLS-1$ //$NON-NLS-2$ return toolchainElements; } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargets.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargets.java new file mode 100644 index 000000000..9f677e33e --- /dev/null +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargets.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ + +package com.espressif.idf.core.toolchain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Class to hold ESP-IDF target information including preview status + * + * @author Kondal Kolipaka + * + */ +public class IDFTargets +{ + private static final Set XTENSA_CHIPS = Set.of("esp32", "esp32s2", "esp32s3"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + private static final String XTENSA = "xtensa"; //$NON-NLS-1$ + private static final String RISCV32 = "riscv32"; //$NON-NLS-1$ + private static final String XTENSA_TOOLCHAIN_ID = XTENSA + "-%s-elf"; //$NON-NLS-1$ + private static final String RISCV32_TOOLCHAIN_ID = RISCV32 + "-esp-elf"; //$NON-NLS-1$ + private static final String XTENSA_UNIFIED_DIR = XTENSA + "-esp-elf"; //$NON-NLS-1$ + private static final String TOOLCHAIN_NAME = "toolchain-%s.cmake"; //$NON-NLS-1$ + private List supportedTargets; + private List previewTargets; + + public IDFTargets() + { + this.supportedTargets = new ArrayList<>(); + this.previewTargets = new ArrayList<>(); + } + + public void addSupportedTarget(String target) + { + supportedTargets.add(new IDFTarget(target, false)); + } + + public void addPreviewTarget(String target) + { + previewTargets.add(new IDFTarget(target, true)); + } + + public List getAllTargets() + { + List allTargets = new ArrayList<>(); + allTargets.addAll(supportedTargets); + allTargets.addAll(previewTargets); + return allTargets; + } + + public List getSupportedTargets() + { + return supportedTargets; + } + + public List getPreviewTargets() + { + return previewTargets; + } + + public boolean hasTarget(String targetName) + { + return getAllTargets().stream().anyMatch(target -> target.getName().equals(targetName)); + } + + /** + * Get a specific target by name + * + * @param targetName Name of the target to find + * @return IDFTarget if found, null otherwise + */ + public IDFTarget getTarget(String targetName) + { + return getAllTargets().stream().filter(target -> target.getName().equals(targetName)).findFirst().orElse(null); + } + + /** + * Get all target names as strings + * + * @return List of target names + */ + public List getAllTargetNames() + { + return getAllTargets().stream().map(IDFTarget::getName).collect(java.util.stream.Collectors.toList()); + } + + /** + * Get supported target names as strings + * + * @return List of supported target names + */ + public List getSupportedTargetNames() + { + return getSupportedTargets().stream().map(IDFTarget::getName).collect(java.util.stream.Collectors.toList()); + } + + /** + * Get preview target names as strings + * + * @return List of preview target names + */ + public List getPreviewTargetNames() + { + return getPreviewTargets().stream().map(IDFTarget::getName).collect(java.util.stream.Collectors.toList()); + } + + /** + * Inner class representing a single IDF target + */ + public static class IDFTarget + { + private final String name; + private final boolean isPreview; + + public IDFTarget(String name, boolean isPreview) + { + this.name = name; + this.isPreview = isPreview; + } + + public String getName() + { + return name; + } + + public boolean isPreview() + { + return isPreview; + } + + /** + * Get the architecture for this target + * + * @return "xtensa" for esp32/esp32s2/esp32s3, "riscv32" for others + */ + public String getArchitecture() + { + return XTENSA_CHIPS.contains(name) ? XTENSA : RISCV32; + } + + /** + * Get the toolchain ID for this target + * + * @return toolchain ID string + */ + public String getToolchainId() + { + return XTENSA_CHIPS.contains(name) ? String.format(XTENSA_TOOLCHAIN_ID, name) : RISCV32_TOOLCHAIN_ID; + } + + /** + * Get the compiler pattern for this target + * + * @return regex pattern for compiler + */ + public String getCompilerPattern() + { + String executableName = getExecutableName(); + + // Support both old and new unified directory structures + String targetSpecificDir = getTargetSpecificDirectoryName(); + String unifiedDir = getUnifiedDirectoryName(); + + // Create pattern that matches either directory structure + return "(?:" + targetSpecificDir + "|" + unifiedDir + ")[\\\\/]+bin[\\\\/]+" + executableName //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + "-gcc(?:\\.exe)?$"; //$NON-NLS-1$ + } + + /** + * Get the debugger pattern for this target + * + * @return regex pattern for debugger + */ + public String getDebuggerPattern() + { + String executableName = getExecutableName(); + return executableName + "-gdb(?:\\.exe)?$"; //$NON-NLS-1$ + } + + /** + * Get the executable name prefix for this target (different from directory structure in ESP-IDF v5.5+) + * + * @return executable name prefix + */ + private String getExecutableName() + { + return XTENSA_CHIPS.contains(name) ? String.format(XTENSA_TOOLCHAIN_ID, name) + : RISCV32_TOOLCHAIN_ID; + } + + private String getTargetSpecificDirectoryName() + { + return XTENSA_CHIPS.contains(name) ? String.format(XTENSA_TOOLCHAIN_ID, name) + : RISCV32_TOOLCHAIN_ID; + } + + private String getUnifiedDirectoryName() + { + return XTENSA_CHIPS.contains(name) ? XTENSA_UNIFIED_DIR : RISCV32_TOOLCHAIN_ID; + } + + /** + * Get the CMake toolchain file name for this target + * + * @return toolchain file name + */ + public String getToolchainFileName() + { + return String.format(TOOLCHAIN_NAME, name); + } + } +} diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargetsReader.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargetsReader.java new file mode 100644 index 000000000..91adc10b6 --- /dev/null +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/toolchain/IDFTargetsReader.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ + +package com.espressif.idf.core.toolchain; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.espressif.idf.core.logging.Logger; + +/** + * Class to read ESP-IDF targets from constants.py file + * + * @author Kondal Kolipaka + * + */ +public class IDFTargetsReader +{ + private static final String CONSTANTS_FILE_PATH = "tools/idf_py_actions/constants.py"; //$NON-NLS-1$ + private static final Pattern SUPPORTED_TARGETS_PATTERN = Pattern + .compile("SUPPORTED_TARGETS\\s*=\\s*\\[([^\\]]*)\\]", Pattern.MULTILINE); //$NON-NLS-1$ + private static final Pattern PREVIEW_TARGETS_PATTERN = Pattern.compile("PREVIEW_TARGETS\\s*=\\s*\\[([^\\]]*)\\]", //$NON-NLS-1$ + Pattern.MULTILINE); + + /** + * Read ESP-IDF targets from the constants.py file + * + * @param idfPath ESP-IDF installation path + * @return IDFTargets object containing all targets + */ + public static IDFTargets readTargetsFromEspIdf(String idfPath) + { + IDFTargets targets = new IDFTargets(); + + if (idfPath == null || idfPath.trim().isEmpty()) + { + Logger.log("ESP-IDF path is null or empty, cannot read targets"); //$NON-NLS-1$ + return targets; + } + + Path constantsFilePath = Paths.get(idfPath, CONSTANTS_FILE_PATH); + + if (!Files.exists(constantsFilePath)) + { + Logger.log("Constants file not found at: " + constantsFilePath); //$NON-NLS-1$ + return targets; + } + + try + { + String content = new String(Files.readAllBytes(constantsFilePath)); + + // Extract supported targets + List supportedTargets = extractTargets(content, SUPPORTED_TARGETS_PATTERN); + for (String target : supportedTargets) + { + targets.addSupportedTarget(target.trim()); + } + + // Extract preview targets + List previewTargets = extractTargets(content, PREVIEW_TARGETS_PATTERN); + for (String target : previewTargets) + { + targets.addPreviewTarget(target.trim()); + } + + Logger.log("Successfully read " + supportedTargets.size() + " supported targets and " //$NON-NLS-1$ //$NON-NLS-2$ + + previewTargets.size() + " preview targets"); //$NON-NLS-1$ + + } + catch (IOException e) + { + Logger.log("Error reading constants file: " + e.getMessage()); //$NON-NLS-1$ + } + catch (Exception e) + { + Logger.log("Unexpected error reading targets: " + e.getMessage()); //$NON-NLS-1$ + } + + return targets; + } + + /** + * Extract target names from the constants.py content using regex pattern + * + * @param content File content as string + * @param pattern Regex pattern to match + * @return List of target names + */ + private static List extractTargets(String content, Pattern pattern) + { + List targets = new ArrayList<>(); + + Matcher matcher = pattern.matcher(content); + if (matcher.find()) + { + String targetsString = matcher.group(1); + // Split by comma and clean up + String[] targetArray = targetsString.split(","); //$NON-NLS-1$ + for (String target : targetArray) + { + // Remove quotes, whitespace and filter empty strings + String cleanTarget = target.replaceAll("['\"\\s]", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + if (!cleanTarget.isEmpty()) + { + targets.add(cleanTarget); + } + } + } + + return targets; + } +} diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/DfuCommandsUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/DfuCommandsUtil.java index 62f01eb6f..033c9234d 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/DfuCommandsUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/DfuCommandsUtil.java @@ -7,9 +7,13 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.cdt.utils.CommandLineUtil; import org.eclipse.core.resources.IProject; @@ -129,17 +133,38 @@ public static void flashDfuBins(ILaunchConfiguration configuration, IProject pro commands.addAll(flashCommandList); File workingDir = null; workingDir = new File(project.getLocationURI()); - Map envMap = new IDFEnvironmentVariables().getSystemEnvMap(); - List strings = new ArrayList<>(envMap.size()); - for (Entry entry : envMap.entrySet()) + Map idfEnvMap = new IDFEnvironmentVariables().getSystemEnvMap(); + + // Disable buffering of output + idfEnvMap.put("PYTHONUNBUFFERED", "1"); //$NON-NLS-1$ //$NON-NLS-2$ + + // Update with the CDT build environment variables + Map environment = new HashMap<>(IDFUtil.getSystemEnv()); + environment.putAll(idfEnvMap); + + Logger.log(environment.toString()); + + // Merge PATH and Path + String mergedPath = Stream.of(environment.get("PATH"), environment.get("Path")).filter(Objects::nonNull) //$NON-NLS-1$ //$NON-NLS-2$ + .collect(Collectors.joining(";")); //$NON-NLS-1$ + + // Remove duplicate Path + if (!mergedPath.isEmpty()) + { + environment.put("PATH", mergedPath); //$NON-NLS-1$ + environment.remove("Path"); //$NON-NLS-1$ + } + + List strings = new ArrayList<>(environment.size()); + for (Entry entry : environment.entrySet()) + { StringBuilder buffer = new StringBuilder(entry.getKey()); buffer.append('=').append(entry.getValue()); // $NON-NLS-1$ strings.add(buffer.toString()); } - - String[] envArray = strings.toArray(new String[strings.size()]); Process p = null; + String[] envArray = strings.toArray(new String[strings.size()]); try { p = DebugPlugin.exec(commands.toArray(new String[0]), workingDir, envArray); diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/FileUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/FileUtil.java index 5e951f4d7..476e89616 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/FileUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/FileUtil.java @@ -8,14 +8,21 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Paths; import java.text.MessageFormat; +import java.util.List; import java.util.Scanner; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; @@ -348,7 +355,7 @@ public static String readFile(String absoluteFilePath) while (scanner.hasNext()) { fileContents.append(scanner.nextLine()); - fileContents.append(System.getProperty("line.separator")); + fileContents.append(System.getProperty("line.separator")); //$NON-NLS-1$ } scanner.close(); } @@ -405,5 +412,82 @@ public static void deleteDirectory(File file) throws IOException throw new IOException("Failed to delete " + file); //$NON-NLS-1$ } } + + public static File createFileWithContentsInDirectory(String fileName, String content, String direcotry) throws IOException + { + java.nio.file.Path directoryPath = java.nio.file.Paths.get(direcotry); + java.nio.file.Path filePath = directoryPath.resolve(fileName); + try (FileWriter writer = new FileWriter(filePath.toFile())) + { + writer.write(content); + } + return filePath.toFile(); + } + + public static void zipDirectory(File directoryToZip, String zipFileName) throws IOException + { + if (directoryToZip == null || !directoryToZip.isDirectory()) + { + throw new IllegalArgumentException("directoryToZip must be an existing directory"); //$NON-NLS-1$ + } + + java.nio.file.Path base = directoryToZip.toPath(); + java.nio.file.Path zipPath = Paths.get(zipFileName); + String rootName = base.getFileName().toString(); + + List paths; + try (Stream walk = Files.walk(base)) + { + paths = walk.sorted((p1, p2) -> { + boolean d1 = Files.isDirectory(p1); + boolean d2 = Files.isDirectory(p2); + if (d1 != d2) + return d1 ? -1 : 1; // dirs before files + String r1 = base.relativize(p1).toString().replace(File.separatorChar, '/'); + String r2 = base.relativize(p2).toString().replace(File.separatorChar, '/'); + return r1.compareTo(r2); + }).toList(); + } + + if (zipPath.getParent() != null) + { + Files.createDirectories(zipPath.getParent()); + } + + try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipPath))) + { + ZipEntry rootEntry = new ZipEntry(rootName + "/"); //$NON-NLS-1$ + rootEntry.setTime(Files.getLastModifiedTime(base).toMillis()); + zos.putNextEntry(rootEntry); + zos.closeEntry(); + + for (java.nio.file.Path p : paths) + { + if (p.equals(base)) + continue; // root already added + + String rel = base.relativize(p).toString().replace(File.separatorChar, '/'); + String entryName = rootName + "/" + rel; //$NON-NLS-1$ + + if (Files.isDirectory(p)) + { + if (!entryName.endsWith("/")) //$NON-NLS-1$ + entryName += "/"; //$NON-NLS-1$ + ZipEntry dirEntry = new ZipEntry(entryName); + dirEntry.setTime(Files.getLastModifiedTime(p).toMillis()); + zos.putNextEntry(dirEntry); + zos.closeEntry(); + } + else + { + ZipEntry fileEntry = new ZipEntry(entryName); + fileEntry.setTime(Files.getLastModifiedTime(p).toMillis()); + zos.putNextEntry(fileEntry); + Files.copy(p, zos); + zos.closeEntry(); + } + } + } + } } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java index 9665b1c8d..d3c985873 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java @@ -717,37 +717,80 @@ public static void updateEspressifPrefPageOpenocdPath() public static String getCurrentTarget() { - IProject project = null; + IProject project = getProjectFromActiveLaunchConfig(); + if (project == null) + { + Logger.log(Messages.IDFUtil_CantFindProjectMsg); + return null; + } + return new SDKConfigJsonReader(project).getValue("IDF_TARGET"); //$NON-NLS-1$ + } + + public static boolean isFlashEncrypted() + { + ILaunchConfiguration configuration; + try + { + configuration = getActiveLaunchConfiguration(); + + return configuration != null + && configuration.getAttribute(IDFLaunchConstants.FLASH_ENCRYPTION_ENABLED, false); + } + catch (CoreException e) + { + Logger.log(e); + } + + return false; + } + + /** + * Returns the active project from the currently selected launch configuration. + */ + public static IProject getProjectFromActiveLaunchConfig() + { try { - ILaunchBarManager launchBarManager = IDFCorePlugin.getService(ILaunchBarManager.class); - ILaunchConfiguration activeConfig = launchBarManager.getActiveLaunchConfiguration(); - if (activeConfig == null || activeConfig.getMappedResources() == null) + ILaunchConfiguration activeConfig = getActiveLaunchConfiguration(); + if (activeConfig == null) + { + return null; + } + + IResource[] resources = activeConfig.getMappedResources(); + if (resources == null || resources.length == 0) { - Logger.log(Messages.IDFUtil_CantFindProjectMsg); - return StringUtil.EMPTY; + Logger.log("No mapped resources in active launch configuration."); //$NON-NLS-1$ + return null; } - project = activeConfig.getMappedResources()[0].getProject(); - Logger.log("Project:: " + project); //$NON-NLS-1$ + + IProject project = resources[0].getProject(); + Logger.log("Active project: " + project); //$NON-NLS-1$ + return project; } catch (CoreException e) { Logger.log(e); + return null; } - return new SDKConfigJsonReader(project).getValue("IDF_TARGET"); //$NON-NLS-1$ } - public static IProject getProjectFromActiveLaunchConfig() throws CoreException + private static ILaunchConfiguration getActiveLaunchConfiguration() throws CoreException { - final ILaunchBarManager launchBarManager = IDFCorePlugin.getService(ILaunchBarManager.class); - ILaunchConfiguration launchConfiguration = launchBarManager.getActiveLaunchConfiguration(); - IResource[] mappedResources = launchConfiguration.getMappedResources(); - if (mappedResources != null && mappedResources[0].getProject() != null) + ILaunchBarManager launchBarManager = IDFCorePlugin.getService(ILaunchBarManager.class); + if (launchBarManager == null) { - return mappedResources[0].getProject(); + Logger.log("LaunchBarManager service not found."); //$NON-NLS-1$ + return null; } - return null; + ILaunchConfiguration activeConfig = launchBarManager.getActiveLaunchConfiguration(); + if (activeConfig == null) + { + Logger.log("No active launch configuration."); //$NON-NLS-1$ + return null; + } + return activeConfig; } public static String getGitExecutablePathFromSystem() @@ -850,10 +893,30 @@ public static String resolveEnvVariable(String path) public static Map getSystemEnv() { - Map env = new HashMap(System.getenv()); + Map env = new HashMap<>(System.getenv()); + String idfToolsPath = Platform.getPreferencesService().getString(IDFCorePlugin.PLUGIN_ID, IDFCorePreferenceConstants.IDF_TOOLS_PATH, IDFCorePreferenceConstants.IDF_TOOLS_PATH_DEFAULT, null); env.put(IDFCorePreferenceConstants.IDF_TOOLS_PATH, idfToolsPath); + + // Merge Homebrew bin paths into PATH + String existingPath = env.getOrDefault("PATH", ""); //$NON-NLS-1$ //$NON-NLS-2$ + StringBuilder newPath = new StringBuilder(); + + String[] brewPaths = { "/usr/local/bin", "/opt/homebrew/bin" }; //$NON-NLS-1$ //$NON-NLS-2$ + + for (String brewPath : brewPaths) + { + if (Files.exists(Paths.get(brewPath)) && !existingPath.contains(brewPath)) + { + newPath.append(brewPath).append(":"); //$NON-NLS-1$ + } + } + + // Append the original PATH at the end + newPath.append(existingPath); + env.put("PATH", newPath.toString()); //$NON-NLS-1$ + return env; } @@ -885,11 +948,14 @@ public static void closeWelcomePage(IWorkbenchWindow activeww) /** * Checks if esp_detect_config.py exists in the expected OpenOCD tools directory. + * * @return true if esp_detect_config.py exists, false otherwise. */ - public static boolean espDetectConfigScriptExists() { + public static boolean espDetectConfigScriptExists() + { String openocdBinDir = getOpenOCDLocation(); - if (StringUtil.isEmpty(openocdBinDir)) { + if (StringUtil.isEmpty(openocdBinDir)) + { return false; } File binDir = new File(openocdBinDir); @@ -900,12 +966,14 @@ public static boolean espDetectConfigScriptExists() { } /** - * Runs the esp_detect_config.py script using the OPENOCD_SCRIPTS environment variable to locate the script and config files. - * Returns the JSON output as a string, or null on error. + * Runs the esp_detect_config.py script using the OPENOCD_SCRIPTS environment variable to locate the script and + * config files. Returns the JSON output as a string, or null on error. */ - public static String runEspDetectConfigScript() { + public static String runEspDetectConfigScript() + { String openocdBinDir = getOpenOCDLocation(); - if (StringUtil.isEmpty(openocdBinDir)) { + if (StringUtil.isEmpty(openocdBinDir)) + { Logger.log("OpenOCD location could not be determined."); //$NON-NLS-1$ return null; } @@ -915,29 +983,34 @@ public static String runEspDetectConfigScript() { File scriptsDir = Paths.get(openocdRoot.getPath(), "share", "openocd", "scripts").toFile(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ File toolsDir = Paths.get(openocdRoot.getPath(), "share", "openocd", "espressif", "tools").toFile(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ File configFile = new File(scriptsDir, "esp-config.json"); //$NON-NLS-1$ - if (!configFile.exists()) { + if (!configFile.exists()) + { Logger.log("esp-config.json not found at expected location: " + configFile.getAbsolutePath()); //$NON-NLS-1$ return null; } - if (!espDetectConfigScriptExists()) { - Logger.log("esp_detect_config.py not found at expected location: " + new File(toolsDir, "esp_detect_config.py").getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$ + if (!espDetectConfigScriptExists()) + { + Logger.log("esp_detect_config.py not found at expected location: " //$NON-NLS-1$ + + new File(toolsDir, "esp_detect_config.py").getAbsolutePath()); //$NON-NLS-1$ return null; } String scriptPath = new File(toolsDir, "esp_detect_config.py").getAbsolutePath(); //$NON-NLS-1$ String configPath = configFile.getAbsolutePath(); String openocdExecutable = Platform.getOS().equals(Platform.OS_WIN32) ? "openocd.exe" : "openocd"; //$NON-NLS-1$ //$NON-NLS-2$ File openocdBin = new File(openocdBinDir, openocdExecutable); - if (!openocdBin.exists()) { + if (!openocdBin.exists()) + { Logger.log("OpenOCD binary not found at expected location."); //$NON-NLS-1$ return null; } - + String idfPythonEnvPath = IDFUtil.getIDFPythonEnvPath(); - if (StringUtil.isEmpty(idfPythonEnvPath)) { + if (StringUtil.isEmpty(idfPythonEnvPath)) + { Logger.log("IDF_PYTHON_ENV_PATH could not be found."); //$NON-NLS-1$ return null; } - + List command = new ArrayList<>(); command.add(idfPythonEnvPath); command.add(scriptPath); @@ -945,16 +1018,20 @@ public static String runEspDetectConfigScript() { command.add(configPath); command.add("--oocd");//$NON-NLS-1$ command.add(openocdBin.getAbsolutePath()); - + Map env = new IDFEnvironmentVariables().getSystemEnvMap(); - try { + try + { IStatus status = new ProcessBuilderFactory().runInBackground(command, null, env); - if (status == null) { + if (status == null) + { Logger.log("esp_detect_config.py did not return a result."); //$NON-NLS-1$ return null; } return status.getMessage(); - } catch (Exception e) { + } + catch (Exception e) + { Logger.log(e); return null; } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LspService.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LspService.java index 4ad33167f..3e55a2595 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LspService.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LspService.java @@ -58,20 +58,6 @@ public void updateAdditionalOptions(String additionalOptions) additionalOptions); } - public void updateLspQueryDrivers() - { - String toolchainPath = IDFUtil.getToolchainExePathForActiveTarget(); - String qualifier = configuration.qualifier(); - if (toolchainPath == null) - { - Logger.log("Toolchain path not found. Skipping update of --query-driver for LSP"); //$NON-NLS-1$ - return; - } - // By passing --query-driver argument to clangd helps to resolve the - // cross-compiler toolchain headers. - InstanceScope.INSTANCE.getNode(qualifier).put(ClangdMetadata.Predefined.queryDriver.identifer(), toolchainPath); - } - public void updateClangdPath() { String clangdPath = IDFUtil.findCommandFromBuildEnvPath(ILSPConstants.CLANGD_EXECUTABLE); @@ -84,6 +70,12 @@ public void updateClangdPath() InstanceScope.INSTANCE.getNode(qualifier).put(ClangdMetadata.Predefined.clangdPath.identifer(), clangdPath); } + public void updateQueryDriver() + { + String qualifier = configuration.qualifier(); + InstanceScope.INSTANCE.getNode(qualifier).put(ClangdMetadata.Predefined.queryDriver.identifer(), "**"); //$NON-NLS-1$ + } + public void updateCompileCommandsDir(String buildDir) { String qualifier = configuration.qualifier(); diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartDynamicVariable.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartDynamicVariable.java index 9b8d56ff9..7e780d4b5 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartDynamicVariable.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartDynamicVariable.java @@ -2,7 +2,8 @@ public enum UartDynamicVariable { - SERIAL_PORT("serial_port"); //$NON-NLS-1$ + SERIAL_PORT("serial_port"), //$NON-NLS-1$ + FLASH_COMMAND("flash_command"); //$NON-NLS-1$ private String value; diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartVariableResolver.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartVariableResolver.java index ea8371aba..7b732b14e 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartVariableResolver.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/variable/UartVariableResolver.java @@ -9,9 +9,11 @@ import org.eclipse.launchbar.core.ILaunchBarManager; import org.eclipse.launchbar.core.target.ILaunchTarget; +import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.LaunchBarTargetConstants; import com.espressif.idf.core.logging.Logger; +import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.StringUtil; public class UartVariableResolver implements IDynamicVariableResolver @@ -34,6 +36,7 @@ private String resolveForDynamicEnum(UartDynamicVariable enumVariable) return switch (enumVariable) { case SERIAL_PORT -> getSerialPort(); // $NON-NLS-1$ + case FLASH_COMMAND -> IDFUtil.isFlashEncrypted() ? IDFConstants.FLASH_ENCRYPTED_CMD : IDFConstants.FLASH_CMD; }; } diff --git a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/internal/SerialFlashLaunchConfigDelegate.java b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/internal/SerialFlashLaunchConfigDelegate.java index f5a8dcbd7..6cf0034d1 100644 --- a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/internal/SerialFlashLaunchConfigDelegate.java +++ b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/internal/SerialFlashLaunchConfigDelegate.java @@ -225,6 +225,7 @@ private void openSerialMonitor(ILaunchConfiguration configuration) throws CoreEx map.put(ITerminalsConnectorConstants.PROP_ENCODING, configuration.getAttribute( IDFLaunchConstants.SERIAL_MONITOR_ENCODING, WorkbenchEncoding.getWorkbenchDefaultEncoding())); map.put(ITerminalsConnectorConstants.PROP_FORCE_NEW, Boolean.FALSE); + map.put(SerialSettings.ENCRYPTION_ATTR, IDFUtil.isFlashEncrypted()); new SerialLauncherDelegate().execute(map, null); } diff --git a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java index c9e05967b..92adbeea5 100644 --- a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java +++ b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java @@ -30,6 +30,7 @@ import com.espressif.idf.core.configparser.EspConfigParser; import com.espressif.idf.core.logging.Logger; import com.espressif.idf.core.util.IDFUtil; +import com.espressif.idf.core.variable.UartDynamicVariable; public class ESPFlashUtil { @@ -75,7 +76,8 @@ public static String getParseableEspFlashCommand(String serialPort) .generateVariableExpression(IDFDynamicVariables.BUILD_DIR.name(), null)); commands.add("-p"); //$NON-NLS-1$ commands.add(serialPort); - commands.add(IDFConstants.FLASH_CMD); + commands.add(VariablesPlugin.getDefault().getStringVariableManager() + .generateVariableExpression(UartDynamicVariable.FLASH_COMMAND.getValue(), null)); return String.join(" ", commands); //$NON-NLS-1$ } diff --git a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/CMakeMainTab2.java b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/CMakeMainTab2.java index e1a1eff3f..6efdfddc4 100644 --- a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/CMakeMainTab2.java +++ b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/CMakeMainTab2.java @@ -54,12 +54,14 @@ import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.ide.IDEEncoding; @@ -79,6 +81,7 @@ @SuppressWarnings("restriction") public class CMakeMainTab2 extends GenericMainTab { + private static final String DOCS_ESPRESSIF_FLASH_ENCRYPTION_HTML = "https://docs.espressif.com/projects/espressif-ide/en/latest/flashdevice.html#customize-flash-arguments"; //$NON-NLS-1$ private static final String DEFAULT_JTAG_CONFIG_OPTIONS = String.format("-s ${%s} ${%s}", //$NON-NLS-1$ OpenocdDynamicVariable.OPENOCD_SCRIPTS, JtagDynamicVariable.JTAG_FLASH_ARGS); private Combo flashOverComboButton; @@ -94,6 +97,7 @@ public class CMakeMainTab2 extends GenericMainTab private TextWithButton dfuArgumentsField; private Button checkOpenSerialMonitorButton; private Combo fEncodingCombo; + private Button flashEncryptionCheckbox; public enum FlashInterface { @@ -230,9 +234,27 @@ protected void createUartComposite(Composite parent) uartAgrumentsField = new TextWithButton(parent, SWT.WRAP | SWT.BORDER); createArgumentComponent(defaultComposite, uartAgrumentsField); + createFlashEncryptionCheckbox(defaultComposite); createVerticalSpacer(defaultComposite, 1); } + private void createFlashEncryptionCheckbox(Composite parent) + { + Group flashGroup = new Group(parent, SWT.NONE); + flashGroup.setText(Messages.CMakeMainTab2_FlashEncryptionGroup); + flashGroup.setLayout(new GridLayout(1, false)); + flashGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + flashEncryptionCheckbox = new Button(flashGroup, SWT.CHECK); + flashEncryptionCheckbox.setText(Messages.CMakeMainTab2_FlashEncryptionCheckbox); + flashEncryptionCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + + Link flashEncryptionNote = new Link(flashGroup, SWT.WRAP); + flashEncryptionNote.setText(Messages.CMakeMainTab2_FlashEncryptionNote); + + flashEncryptionNote.addListener(SWT.Selection, e -> Program.launch(DOCS_ESPRESSIF_FLASH_ENCRYPTION_HTML)); + } + protected void createJtagflashComposite(Composite parent) { @@ -541,6 +563,8 @@ public void performApply(ILaunchConfigurationWorkingCopy configuration) wc.setAttribute(IDFLaunchConstants.OPEN_SERIAL_MONITOR, checkOpenSerialMonitorButton.getSelection()); if (checkOpenSerialMonitorButton.getSelection()) wc.setAttribute(IDFLaunchConstants.SERIAL_MONITOR_ENCODING, fEncodingCombo.getText()); + wc.setAttribute(IDFLaunchConstants.FLASH_ENCRYPTION_ENABLED, flashEncryptionCheckbox.getSelection()); + wc.doSave(); } catch (CoreException e) @@ -553,6 +577,7 @@ public void performApply(ILaunchConfigurationWorkingCopy configuration) public void initializeFrom(ILaunchConfiguration configuration) { super.initializeFrom(configuration); + updateFlashEncryptionGroup(configuration); updateStartSerialMonitorGroup(configuration); updateProjetFromConfig(configuration); updateFlashOverStatus(configuration); @@ -560,6 +585,20 @@ public void initializeFrom(ILaunchConfiguration configuration) switchUI(FlashInterface.values()[flashOverComboButton.getSelectionIndex()]); } + private void updateFlashEncryptionGroup(ILaunchConfiguration configuration) + { + boolean isFlashEncryptionEnabled = false; + try + { + isFlashEncryptionEnabled = configuration.getAttribute(IDFLaunchConstants.FLASH_ENCRYPTION_ENABLED, false); + } + catch (CoreException e) + { + Logger.log(e); + } + flashEncryptionCheckbox.setSelection(isFlashEncryptionEnabled); + } + private void updateProjetFromConfig(ILaunchConfiguration configuration) { String projectName = StringUtil.EMPTY; diff --git a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/Messages.java b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/Messages.java index e9e5d57aa..bbad79c55 100644 --- a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/Messages.java +++ b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/Messages.java @@ -53,6 +53,9 @@ public class Messages extends NLS public static String CMakeMainTab2_SerialMonitorGroup; public static String CMakeMainTab2_SerialMonitorEncodingLbl; public static String jtagGroupLbl; + public static String CMakeMainTab2_FlashEncryptionGroup; + public static String CMakeMainTab2_FlashEncryptionCheckbox; + public static String CMakeMainTab2_FlashEncryptionNote; public static String TargetPortUpdatingMessage; public static String TargetPortInformationMessage; diff --git a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/messages.properties b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/messages.properties index 2d3b430d0..704cca83a 100644 --- a/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/messages.properties +++ b/bundles/com.espressif.idf.launch.serial.ui/src/com/espressif/idf/launch/serial/ui/internal/messages.properties @@ -36,6 +36,10 @@ CMakeMainTab2_SettingTargetJob=Setting a target in launch bar... CMakeMainTab2_SerialMonitorBtn=Open Serial Monitor After Flashing CMakeMainTab2_SerialMonitorGroup=Serial Monitor Settings CMakeMainTab2_SerialMonitorEncodingLbl=Encoding: +CMakeMainTab2_FlashEncryptionGroup=Flash Encryption +CMakeMainTab2_FlashEncryptionCheckbox=Enable Flash Encryption +CMakeMainTab2_FlashEncryptionNote=Note: Do a normal flash with CONFIG_SECURE_FLASH_ENC_ENABLED before enabling. See documentation for details. + IDFLaunchTargetNotFoundMsg1=IDF Launch Target with IDFLaunchTargetNotFoundMsg2=\ is not found. diff --git a/bundles/com.espressif.idf.lsp/src/com/espressif/idf/lsp/preferences/IDFClangdMetadataDefaults.java b/bundles/com.espressif.idf.lsp/src/com/espressif/idf/lsp/preferences/IDFClangdMetadataDefaults.java index 1a8f9f412..fb96af268 100644 --- a/bundles/com.espressif.idf.lsp/src/com/espressif/idf/lsp/preferences/IDFClangdMetadataDefaults.java +++ b/bundles/com.espressif.idf.lsp/src/com/espressif/idf/lsp/preferences/IDFClangdMetadataDefaults.java @@ -1,6 +1,5 @@ package com.espressif.idf.lsp.preferences; -import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -12,7 +11,6 @@ import org.osgi.service.component.annotations.Component; import com.espressif.idf.core.ILSPConstants; -import com.espressif.idf.core.toolchain.ESPToolChainManager; import com.espressif.idf.core.util.IDFUtil; @Component(property = { "service.ranking:Integer=0" }) @@ -33,9 +31,8 @@ protected List> definePreferences() var clangdMetadataWithDefault = wrapWithCustomDefaultValue(clangdPath, ClangdMetadata.Predefined.clangdPath); - ESPToolChainManager toolChainManager = new ESPToolChainManager(); - String defaultIdfQueryDriver = Optional.ofNullable(toolChainManager.findCompiler("esp32")) //$NON-NLS-1$ - .map(File::getAbsolutePath).orElse(""); //$NON-NLS-1$ + // Allow clangd to use the driver specified in compile_commands.json + String defaultIdfQueryDriver = "**"; //$NON-NLS-1$ var queryDriverMetadataWithDefault = wrapWithCustomDefaultValue(defaultIdfQueryDriver, ClangdMetadata.Predefined.queryDriver); diff --git a/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/core/IDFMonitor.java b/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/core/IDFMonitor.java index ce8f17126..0e0c31c5b 100644 --- a/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/core/IDFMonitor.java +++ b/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/core/IDFMonitor.java @@ -40,13 +40,16 @@ public class IDFMonitor private IProject project; private String filterOptions; private int serverPort; + private boolean encryptionOption; - public IDFMonitor(IProject project, String port, String filterOptions, String pythonBinPath, int serverPort) + public IDFMonitor(IProject project, String port, String filterOptions, boolean encryptionOption, + String pythonBinPath, int serverPort) { this.project = project; this.port = port; this.pythonBinPath = pythonBinPath; this.filterOptions = filterOptions; + this.encryptionOption = encryptionOption; this.serverPort = serverPort; } @@ -71,6 +74,10 @@ private List commandArgsWithSocketServer() args.add(port); args.add("-b"); //$NON-NLS-1$ args.add(getMonitorBaudRate()); + if (encryptionOption) + { + args.add("--encrypted"); //$NON-NLS-1$ + } args.add("--ws"); //$NON-NLS-1$ args.add("ws://localhost:".concat(String.valueOf(serverPort))); //$NON-NLS-1$ args.add(getElfFilePath(project).toString()); diff --git a/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/handlers/SerialMonitorHandler.java b/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/handlers/SerialMonitorHandler.java index f2fdf797e..dd4c2375f 100644 --- a/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/handlers/SerialMonitorHandler.java +++ b/bundles/com.espressif.idf.serial.monitor/src/com/espressif/idf/serial/monitor/handlers/SerialMonitorHandler.java @@ -17,13 +17,16 @@ public class SerialMonitorHandler private String serialPort; private String filterOptions; private int serverPort; + private boolean encryptionOption; - public SerialMonitorHandler(IProject project, String serialPort, String filterOptions, int serverPort) + public SerialMonitorHandler(IProject project, String serialPort, String filterOptions, boolean encryptionOption, + int serverPort) { this.project = project; this.serialPort = serialPort; this.filterOptions = filterOptions; this.serverPort = serverPort; + this.encryptionOption = encryptionOption; } public Process invokeIDFMonitor() @@ -31,7 +34,8 @@ public Process invokeIDFMonitor() // python path String pythonPath = IDFUtil.getIDFPythonEnvPath(); - IDFMonitor monitor = new IDFMonitor(project, serialPort, filterOptions, pythonPath, serverPort); + IDFMonitor monitor = new IDFMonitor(project, serialPort, filterOptions, encryptionOption, pythonPath, + serverPort); try { return monitor.start(); diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialConnector.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialConnector.java index 07691b62f..afa0ce315 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialConnector.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialConnector.java @@ -28,55 +28,65 @@ import com.espressif.idf.core.util.StringUtil; import com.espressif.idf.terminal.connector.serial.activator.Activator; -public class SerialConnector extends TerminalConnectorImpl { +public class SerialConnector extends TerminalConnectorImpl +{ private SerialSettings settings = new SerialSettings(); protected Process process; protected Thread thread; protected IProject project; protected String filterOptions; + protected boolean encryptionOption; protected ITerminalControl control; private SerialPortHandler serialPort; private static Set openPorts = new HashSet<>(); - public static boolean isOpen(String portName) { + public static boolean isOpen(String portName) + { return openPorts.contains(portName); } @Override - public OutputStream getTerminalToRemoteStream() { + public OutputStream getTerminalToRemoteStream() + { return process.getOutputStream(); } - public SerialSettings getSettings() { + public SerialSettings getSettings() + { return settings; } @Override - public String getSettingsSummary() { + public String getSettingsSummary() + { return settings.getSummary(); } @Override - public void load(ISettingsStore store) { + public void load(ISettingsStore store) + { settings.load(store); } @Override - public void save(ISettingsStore store) { + public void save(ISettingsStore store) + { settings.save(store); } @Override - public void connect(ITerminalControl control) { + public void connect(ITerminalControl control) + { super.connect(control); this.control = control; - //Get selected project - which is required for IDF Monitor + // Get selected project - which is required for IDF Monitor project = settings.getProject(); - if (project == null) { + if (project == null) + { String message = "project can't be null. Make sure you select a project before launch a serial monitor"; //$NON-NLS-1$ Activator.log(new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), message, null)); return; @@ -85,7 +95,7 @@ public void connect(ITerminalControl control) { String portName = settings.getPortName(); filterOptions = settings.getFilterText(); filterOptions = StringUtil.isEmpty(filterOptions) ? StringUtil.EMPTY : filterOptions; - + encryptionOption = settings.getEncryptionOption(); serialPort = new SerialPortHandler(portName, this, project); serialPort.open(); @@ -94,13 +104,18 @@ public void connect(ITerminalControl control) { } @Override - protected void doDisconnect() { + protected void doDisconnect() + { - if (serialPort != null && serialPort.isOpen()) { + if (serialPort != null && serialPort.isOpen()) + { openPorts.remove(serialPort.getPortName()); - try { + try + { serialPort.close(); - } catch (IOException e) { + } + catch (IOException e) + { Activator.log(e); } } diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialPortHandler.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialPortHandler.java index 17d7875fb..3fda8468b 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialPortHandler.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialPortHandler.java @@ -24,8 +24,6 @@ public class SerialPortHandler private boolean isOpen; private boolean isPaused; private Object pauseMutex = new Object(); - private IProject project; - private static final Map>> openPorts = new HashMap<>(); private Process process; @@ -37,14 +35,7 @@ public class SerialPortHandler private static String adjustPortName(String portName) { - if (System.getProperty("os.name").startsWith("Windows") && !portName.startsWith("\\\\.\\")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - { - return portName; // $NON-NLS-1$ - } - else - { - return portName; - } + return portName; } /** @@ -88,7 +79,6 @@ public SerialPortHandler(String portName, SerialConnector serialConnector, IProj { this.portName = adjustPortName(portName); this.serialConnector = serialConnector; - this.project = project; this.serverMessageHandler = new SocketServerMessageHandler(serialConnector, project); } @@ -123,7 +113,7 @@ public synchronized void open() // Hook IDF Monitor with the CDT serial monitor SerialMonitorHandler serialMonitorHandler = new SerialMonitorHandler(serialConnector.project, portName, - serialConnector.filterOptions, serverPort); + serialConnector.filterOptions, serialConnector.encryptionOption, serverPort); process = serialMonitorHandler.invokeIDFMonitor(); serialConnector.process = process; thread = new Thread() diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialSettings.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialSettings.java index 3ab791dc4..444571157 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialSettings.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/connector/SerialSettings.java @@ -19,67 +19,92 @@ import com.espressif.idf.core.util.StringUtil; -public class SerialSettings { +public class SerialSettings +{ public static final String PORT_NAME_ATTR = "cdtserial.portName"; //$NON-NLS-1$ public static final String MONITOR_FILTER = "idf.monitor.filter"; //$NON-NLS-1$ public static final String SELECTED_PROJECT_ATTR = "idf.monitor.project"; //$NON-NLS-1$ + public static final String ENCRYPTION_ATTR = "idf.monitor.encryption"; //$NON-NLS-1$ private String portName; private String filterText; private String selectedProject; + private boolean encryptionOption; /** * Load information into the RemoteSettings object. */ - public void load(ISettingsStore store) { + public void load(ISettingsStore store) + { portName = store.get(PORT_NAME_ATTR, ""); //$NON-NLS-1$ filterText = store.get(MONITOR_FILTER, ""); //$NON-NLS-1$ selectedProject = store.get(SELECTED_PROJECT_ATTR, ""); //$NON-NLS-1$ + encryptionOption = Boolean.parseBoolean(store.get(ENCRYPTION_ATTR, "false")); //$NON-NLS-1$ } /** * Extract information from the RemoteSettings object. */ - public void save(ISettingsStore store) { + public void save(ISettingsStore store) + { store.put(PORT_NAME_ATTR, portName); store.put(MONITOR_FILTER, filterText); store.put(SELECTED_PROJECT_ATTR, selectedProject); + store.put(ENCRYPTION_ATTR, Boolean.toString(encryptionOption)); } - public String getPortName() { + public String getPortName() + { return portName; } - public String getFilterText() { + public String getFilterText() + { return filterText; } - public String getProjectName() { + public String getProjectName() + { return selectedProject; } - public IProject getProject() { + public boolean getEncryptionOption() + { + return encryptionOption; + } + + public IProject getProject() + { return !StringUtil.isEmpty(selectedProject) ? ResourcesPlugin.getWorkspace().getRoot().getProject(selectedProject) : null; } - public void setPortName(String portName) { + public void setPortName(String portName) + { this.portName = portName; } - public void setFilterText(String filterText) { + public void setFilterText(String filterText) + { this.filterText = filterText; } - public String getSummary() { + public String getSummary() + { return portName; } - public void setProject(String projectName) { + public void setProject(String projectName) + { this.selectedProject = projectName; } + public void setEncryptionOption(boolean encryptionOption) + { + this.encryptionOption = encryptionOption; + } + } diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialConfigPanel.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialConfigPanel.java index 1d3858486..6fd72517a 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialConfigPanel.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialConfigPanel.java @@ -26,17 +26,20 @@ import com.espressif.idf.terminal.connector.serial.connector.SerialConnector; import com.espressif.idf.terminal.connector.serial.connector.SerialSettings; -public class SerialConfigPanel extends AbstractExtendedConfigurationPanel { +public class SerialConfigPanel extends AbstractExtendedConfigurationPanel +{ private SerialSettings settings; private SerialSettingsPage page; - public SerialConfigPanel(IConfigurationPanelContainer container) { + public SerialConfigPanel(IConfigurationPanelContainer container) + { super(container); } @Override - public void setupPanel(Composite parent) { + public void setupPanel(Composite parent) + { Composite panel = new Composite(parent, SWT.NONE); panel.setLayout(new GridLayout()); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); @@ -53,8 +56,10 @@ public void setupPanel(Composite parent) { } @Override - public void extractData(Map data) { - if (data == null) { + public void extractData(Map data) + { + if (data == null) + { return; } @@ -62,38 +67,48 @@ public void extractData(Map data) { data.put(SerialSettings.PORT_NAME_ATTR, settings.getPortName()); data.put(SerialSettings.MONITOR_FILTER, settings.getFilterText()); data.put(SerialSettings.SELECTED_PROJECT_ATTR, settings.getProjectName()); + data.put(SerialSettings.ENCRYPTION_ATTR, settings.getEncryptionOption()); - if (getEncoding() != null) { + if (getEncoding() != null) + { data.put(ITerminalsConnectorConstants.PROP_ENCODING, getEncoding()); } } @Override - public void setupData(Map data) { - if (data == null) { + public void setupData(Map data) + { + if (data == null) + { return; } settings.setPortName((String) data.get(SerialSettings.PORT_NAME_ATTR)); settings.setFilterText((String) data.get(SerialSettings.MONITOR_FILTER)); - + Boolean encryptionValue = (Boolean) data.get(SerialSettings.ENCRYPTION_ATTR); + settings.setEncryptionOption(encryptionValue != null && encryptionValue); String encoding = (String) data.get(ITerminalsConnectorConstants.PROP_ENCODING); - if (encoding != null) { + if (encoding != null) + { setEncoding(encoding); } } @Override - protected void saveSettingsForHost(boolean add) { + protected void saveSettingsForHost(boolean add) + { } @Override - protected void fillSettingsForHost(String host) { + protected void fillSettingsForHost(String host) + { } @Override - protected String getHostFromSettings() { - if (page != null) { + protected String getHostFromSettings() + { + if (page != null) + { page.saveSettings(); return settings.getPortName(); } diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialSettingsPage.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialSettingsPage.java index 78628829b..1917980dd 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialSettingsPage.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/controls/SerialSettingsPage.java @@ -28,6 +28,7 @@ import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; @@ -60,6 +61,8 @@ public class SerialSettingsPage extends AbstractSettingsPage private String lastUsedSerialPort; private Text filterText; private String filterConfig; + private Button encryptionCheckbox; + private boolean encryptionOption; public SerialSettingsPage(SerialSettings settings, IConfigurationPanel panel) { @@ -71,6 +74,7 @@ public SerialSettingsPage(SerialSettings settings, IConfigurationPanel panel) this.getClass().getSimpleName()); dialogSettings.get(SerialSettings.PORT_NAME_ATTR); filterConfig = dialogSettings.get(SerialSettings.MONITOR_FILTER); + encryptionOption = Boolean.parseBoolean(dialogSettings.get(SerialSettings.ENCRYPTION_ATTR)); lastUsedSerialPort = getLastUsedSerialPort(); @@ -152,6 +156,12 @@ public void widgetSelected(SelectionEvent e) filterText = new Text(comp, SWT.SINGLE | SWT.BORDER); filterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + encryptionCheckbox = new Button(comp, SWT.CHECK); + encryptionCheckbox.setText(Messages.SerialSettingsPage_EncryptionOption); + encryptionCheckbox.setToolTipText(Messages.SerialSettingsPage_EncryptionOptionTooltip1 + System.lineSeparator() + + Messages.SerialSettingsPage_EncryptionOptionTooltip2); + encryptionCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + loadSettings(); } @@ -192,6 +202,8 @@ else if (portCombo.getItemCount() > 0) { this.filterText.setText(filterConfig); } + + this.encryptionCheckbox.setSelection(encryptionOption); } @Override @@ -200,10 +212,12 @@ public void saveSettings() settings.setPortName(portCombo.getText()); settings.setFilterText(filterText.getText().trim()); settings.setProject(projectCombo.getText()); + settings.setEncryptionOption(encryptionCheckbox.getSelection()); dialogSettings.put(SerialSettings.SELECTED_PROJECT_ATTR, projectCombo.getText()); dialogSettings.put(SerialSettings.PORT_NAME_ATTR, portCombo.getText()); dialogSettings.put(SerialSettings.MONITOR_FILTER, filterText.getText().trim()); + dialogSettings.put(SerialSettings.ENCRYPTION_ATTR, encryptionCheckbox.getSelection()); } @Override diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/SerialLauncherDelegate.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/SerialLauncherDelegate.java index fdb5e48af..28e3277eb 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/SerialLauncherDelegate.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/SerialLauncherDelegate.java @@ -30,25 +30,30 @@ import com.espressif.idf.terminal.connector.serial.connector.SerialSettings; import com.espressif.idf.terminal.connector.serial.controls.SerialConfigPanel; -public class SerialLauncherDelegate extends AbstractLauncherDelegate { +public class SerialLauncherDelegate extends AbstractLauncherDelegate +{ @Override - public boolean needsUserConfiguration() { + public boolean needsUserConfiguration() + { return true; } @Override - public IConfigurationPanel getPanel(IConfigurationPanelContainer container) { + public IConfigurationPanel getPanel(IConfigurationPanelContainer container) + { return new SerialConfigPanel(container); } @Override - public ITerminalConnector createTerminalConnector(Map properties) { + public ITerminalConnector createTerminalConnector(Map properties) + { Assert.isNotNull(properties); // Check for the terminal connector id String connectorId = (String) properties.get(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID); - if (connectorId == null) { + if (connectorId == null) + { connectorId = "com.espressif.idf.terminal.connector.serial.SerialConnector"; //$NON-NLS-1$ } @@ -57,13 +62,16 @@ public ITerminalConnector createTerminalConnector(Map properties settings.setPortName((String) properties.get(SerialSettings.PORT_NAME_ATTR)); settings.setFilterText((String) properties.get(SerialSettings.MONITOR_FILTER)); settings.setProject((String) properties.get(SerialSettings.SELECTED_PROJECT_ATTR)); + Boolean encryptionOption = (Boolean) properties.get(SerialSettings.ENCRYPTION_ATTR); + settings.setEncryptionOption(encryptionOption != null && encryptionOption); // Construct the terminal settings store ISettingsStore store = new SettingsStore(); settings.save(store); // Construct the terminal connector instance ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector(connectorId); - if (connector != null) { + if (connector != null) + { // Apply default settings connector.setDefaultSettings(); // And load the real settings @@ -74,7 +82,8 @@ public ITerminalConnector createTerminalConnector(Map properties } @Override - public void execute(Map properties, Done done) { + public void execute(Map properties, Done done) + { Assert.isNotNull(properties); // Set the terminal tab title @@ -83,14 +92,16 @@ public void execute(Map properties, Done done) { // Force a new terminal tab each time it is launched, if not set otherwise from outside // TODO need a command shell service routing to get this - if (!properties.containsKey(ITerminalsConnectorConstants.PROP_FORCE_NEW)) { + if (!properties.containsKey(ITerminalsConnectorConstants.PROP_FORCE_NEW)) + { properties.put(ITerminalsConnectorConstants.PROP_FORCE_NEW, Boolean.TRUE); } // Get the terminal service ITerminalService terminal = TerminalServiceFactory.getService(); // If not available, we cannot fulfill this request - if (terminal != null) { + if (terminal != null) + { terminal.openConsole(properties, done); } } diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.java index f8d6b9ef0..6fe96948b 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.java @@ -34,6 +34,9 @@ public class Messages extends NLS // **** Declare externalized string id's down here ***** + public static String SerialSettingsPage_EncryptionOption; + public static String SerialSettingsPage_EncryptionOptionTooltip1; + public static String SerialSettingsPage_EncryptionOptionTooltip2; public static String SerialSettingsPage_FilterOptions; public static String SerialSettingsPage_ProjectName; public static String SerialTerminalSettingsPage_SerialPort; diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.properties b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.properties index f04f18d51..27162ce11 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.properties +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/nls/Messages.properties @@ -11,6 +11,9 @@ # QNX Software Systems - Initial API and implementation ############################################################################### +SerialSettingsPage_EncryptionOption=Enable Flash Encryption +SerialSettingsPage_EncryptionOptionTooltip1=Enable Flash Encryption. Ctrl+F and Ctrl+A will trigger the encrypted-flash and encrypted-app-flash commands. +SerialSettingsPage_EncryptionOptionTooltip2=To use encrypted flash, make sure flash encryption is enabled in sdkconfig. SerialSettingsPage_FilterOptions=Filter Options: SerialSettingsPage_ProjectName=Project name: SerialTerminalSettingsPage_SerialPort=Serial port: diff --git a/bundles/com.espressif.idf.ui/plugin.xml b/bundles/com.espressif.idf.ui/plugin.xml index e194e00c2..2efd03d66 100644 --- a/bundles/com.espressif.idf.ui/plugin.xml +++ b/bundles/com.espressif.idf.ui/plugin.xml @@ -113,6 +113,17 @@ style="push"> + + + + + + { + result[0] = findConsole(name); + }); + return result[0]; + } + + private MessageConsole findConsole(String name) + { + ConsolePlugin plugin = ConsolePlugin.getDefault(); + IConsoleManager conMan = plugin.getConsoleManager(); + IConsole[] existing = conMan.getConsoles(); + for (IConsole ic : existing) + { + if (name.equals(ic.getName())) + { + return (MessageConsole) ic; + } + } + // No console found, create a new one (no icon) + MessageConsole myConsole = new MessageConsole(name, null); + conMan.addConsoles(new IConsole[] { myConsole }); + return myConsole; + } +} diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/templates/NewProjectCreationWizardPage.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/templates/NewProjectCreationWizardPage.java index 30c40ce54..2be453566 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/templates/NewProjectCreationWizardPage.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/templates/NewProjectCreationWizardPage.java @@ -32,8 +32,9 @@ import org.eclipse.ui.internal.ide.dialogs.ProjectContentsLocationArea; import org.eclipse.ui.internal.ide.dialogs.ProjectContentsLocationArea.IErrorMessageReporter; -import com.espressif.idf.core.configparser.EspConfigParser; import com.espressif.idf.core.logging.Logger; +import com.espressif.idf.core.toolchain.IDFTargets; +import com.espressif.idf.core.toolchain.IDFTargetsReader; import com.espressif.idf.core.util.IDFUtil; /** @@ -77,9 +78,9 @@ private void createProjectTargetSelection(Composite container) mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Label label = new Label(mainComposite, SWT.NONE); label.setText(Messages.NewProjectTargetSelection_Label); - EspConfigParser parser = new EspConfigParser(); + IDFTargets idfTargets = IDFTargetsReader.readTargetsFromEspIdf(IDFUtil.getIDFPath()); targetCombo = new Combo(mainComposite, SWT.READ_ONLY); - targetCombo.setItems(parser.getTargets().toArray(new String[0])); + targetCombo.setItems(idfTargets.getAllTargetNames().toArray(String[]::new)); targetCombo.select(0); targetCombo.setToolTipText(Messages.NewProjectTargetSelection_Tooltip); } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/ToolsActivationJob.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/ToolsActivationJob.java index 63c0c994d..9859fae25 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/ToolsActivationJob.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/ToolsActivationJob.java @@ -26,6 +26,7 @@ import com.espressif.idf.core.IDFEnvironmentVariables; import com.espressif.idf.core.logging.Logger; import com.espressif.idf.core.toolchain.ESPToolChainManager; +import com.espressif.idf.core.toolchain.IDFTargetsReader; import com.espressif.idf.core.tools.vo.IDFToolSet; import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.LspService; @@ -95,7 +96,10 @@ protected IStatus run(IProgressMonitor monitor) toolSetConfigurationManager.export(idfToolSet); console.println("Tools Activated"); - new LspService().updateClangdPath(); + LspService lspService = new LspService(); + lspService.updateClangdPath(); + lspService.updateQueryDriver(); + console.println(Messages.ClangdPreferences_UpdatedMsg); Preferences scopedPreferenceStore = InstanceScope.INSTANCE.getNode(UIPlugin.PLUGIN_ID); scopedPreferenceStore.putBoolean(INSTALL_TOOLS_FLAG, true); try @@ -172,14 +176,10 @@ public void run() private void setUpToolChainsAndTargets() { - IStatus status = loadTargetsAvailableFromIdfInCurrentToolSet(); - if (status.getSeverity() == IStatus.ERROR) - { - Logger.log("Unable to get IDF targets from current toolset"); - return; - } + // Get current active idf + String idfPath = IDFUtil.getIDFPath(); + List targets = IDFTargetsReader.readTargetsFromEspIdf(idfPath).getAllTargetNames(); - List targets = extractTargets(status.getMessage()); ESPToolChainManager espToolChainManager = new ESPToolChainManager(); espToolChainManager.removeLaunchTargetsNotPresent(targets); espToolChainManager.removeCmakeToolChains(); diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/Messages.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/Messages.java index 68103c68c..f468acf38 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/Messages.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/Messages.java @@ -73,7 +73,10 @@ public class Messages extends NLS public static String SbomCommandDialog_StatusCantBeNullErrorMsg; public static String ToolsInstallationJobCompletedMessage; - + + public static String BugReportHandler_CompletedBugReportMsg; + public static String ClangdPreferences_UpdatedMsg; + static { // initialize resource bundle diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/messages.properties b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/messages.properties index a449ac7a9..ddec0b633 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/messages.properties +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/update/messages.properties @@ -65,3 +65,7 @@ SbomCommandDialog_SbomTitle=Software Bill of Materials Tool SbomCommandDialog_StatusCantBeNullErrorMsg=Operation status cannot be null. Please ensure the operation was executed correctly. ToolsInstallationJobCompletedMessage=Tool installation has been successfully completed. To utilize specific tools, please use \"Set Active\" button + + +BugReportHandler_CompletedBugReportMsg=\n\n================================\nBug report completed. You can find the report at: %s \n=================================\n +ClangdPreferences_UpdatedMsg=Clangd preferences updated: clangd path and query driver path have been set. diff --git a/docs/_static/docs_version.js b/docs/_static/docs_version.js new file mode 100644 index 000000000..23148d1e2 --- /dev/null +++ b/docs/_static/docs_version.js @@ -0,0 +1,9 @@ +var DOCUMENTATION_VERSIONS = { + DEFAULTS: { has_targets: false, + supported_targets: [ "esp32" ] + }, + VERSIONS: [ + { name: "latest" }, + { name: "release-v4.0.0", pre_release: true } + ] +}; \ No newline at end of file diff --git a/docs/en/flashdevice.rst b/docs/en/flashdevice.rst index 8440a8488..24742de29 100644 --- a/docs/en/flashdevice.rst +++ b/docs/en/flashdevice.rst @@ -13,6 +13,8 @@ Flash operation can be initiated with just a click of a launch button |run_icon| To provide customized flash arguments, please follow :ref:`this link ` for further instructions. +To enable flash encryption, please see the :ref:`Flash Encryption guide `. + To configure flashing via JTAG, please refer to this :ref:`JTAG Flashing guide ` .. _customizeLaunchConfig: @@ -20,23 +22,64 @@ To configure flashing via JTAG, please refer to this :ref:`JTAG Flashing guide < Customize Flash Arguments ------------------------------- -To provide the customized launch configuration and flash arguments, please follow the step-by-step instructions below. +To provide the customized launch configuration and flash arguments, follow the steps below: #. Click on the ``Launch Configuration`` edit button. #. Switch to the ``Main`` tab. #. Specify the ``Location`` where this application has to run. Since ``idf.py`` is a Python file, configure the Python system path. Example: ``${system_path:python}``. #. Specify the ``Working directory`` of the application. Example: ``${workspace_loc:/hello_world}``. -#. In additional arguments, provide a flashing command that will run in the specified working directory. -#. The flash command looks like this: ``/Users/user-name/esp/esp-idf/tools/idf.py -p /dev/cu.SLAB_USBtoUART flash``. -#. Click OK to save the settings. +#. In the **Arguments** field (see **1** in the image), the default value uses **dynamic variables**: + + ``${IDF_PY} -B ${BUILD_DIR} -p ${serial_port} ${flash_command}`` + + This default setup automatically adapts to your project and board, so usually no manual changes are needed. + + - Use the **Preview icon** (see **2**) to switch between showing resolved values and the raw dynamic variables. + - The field is **modifiable only** when dynamic variables are shown (not resolved). + - If you are migrating from an older plugin version and the field does not contain dynamic variables, click **Restore Defaults** (see **3**) to reset it. + +#. Click **OK** to save the settings. #. Click on the ``Launch`` icon to flash the application to the selected board. -.. image:: ../../media/11_launch_configuration.png +.. image:: ../../media/launch_configuration.png :alt: Launch configuration .. image:: ../../media/12_flashing.png :alt: Flashing process + +.. _flashEncryption: + +Flash Encryption +---------------- + +.. warning:: + + Enabling flash encryption is an **irreversible operation**. + If configured incorrectly, the board may become permanently unusable. + Proceed with caution and only enable this option if you fully understand its implications. + +To enable flash encryption in ESP-IDF, follow these steps: + +#. Open ``sdkconfig`` and enable the ``Enable flash encryption on boot`` option. + +.. image:: ../../media/flash_encryption_1.png + :alt: Flash encryption sdkconfig + +#. Perform a normal flash of the application. +#. Open the **Launch Configuration** dialog, edit the configuration, and check the **Enable Flash Encryption** box. + +.. image:: ../../media/flash_encryption_2.png + :alt: Flash encryption checkbox + +#. Flash the application again. + +Once enabled, flash encryption will automatically secure the contents of the flash memory according to ESP-IDF settings. + +For more details, please refer to the official +`ESP-IDF Flash Encryption documentation `_. + + .. _JTAGFlashing: Upload Application via JTAG diff --git a/docs/en/marketplaceupdate.rst b/docs/en/marketplaceupdate.rst index 6b3e061c5..d8b3fb205 100644 --- a/docs/en/marketplaceupdate.rst +++ b/docs/en/marketplaceupdate.rst @@ -10,7 +10,7 @@ The Espressif-IDE Eclipse Plugin can be installed using the update site URL, Ecl - :ref:`install_idf_eclipse_plugin_local_archive` - :ref:`upgradePlugins` - :ref:`upgradeEspressifIdeDependencies` - +- :ref:`troubleshooting_missing_items` .. _installUpdateSiteURL: @@ -116,3 +116,27 @@ To resolve this: .. image:: ../../media/Resolve_update_error.png This will allow the installer to update or replace any conflicting components and continue the installation successfully. + + + +.. _troubleshooting_missing_items: + +Troubleshooting: "Cannot complete the install because one or more required items could not be found" +----------------------------------------------------------------------------------------------------- + + +This usually happens when the Eclipse Platform update site is not enabled. +To resolve this issue: + +1. Go to ``Help`` > ``Install New Software``. +2. Click **Manage**. +3. Make sure the option for the **Eclipse Platform - Latest Release Update Site** is enabled. + +.. image:: ../../media/Resolve_update_error_2.png + +4. Apply the changes and close the dialog. +5. Then go to ``Help`` > ``Check for Updates`` and proceed with updating the IDE and its dependencies. + +.. note:: + Enabling the Eclipse Platform update site ensures that all required dependencies can be resolved during installation or upgrade. + diff --git a/features/com.espressif.idf.feature/feature.xml b/features/com.espressif.idf.feature/feature.xml index fb7696c9c..f5bb959cb 100644 --- a/features/com.espressif.idf.feature/feature.xml +++ b/features/com.espressif.idf.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/media/Resolve_update_error_2.png b/media/Resolve_update_error_2.png new file mode 100644 index 000000000..0e80db1d6 Binary files /dev/null and b/media/Resolve_update_error_2.png differ diff --git a/media/flash_encryption_1.png b/media/flash_encryption_1.png new file mode 100644 index 000000000..3e67e9405 Binary files /dev/null and b/media/flash_encryption_1.png differ diff --git a/media/flash_encryption_2.png b/media/flash_encryption_2.png new file mode 100644 index 000000000..55a8ff040 Binary files /dev/null and b/media/flash_encryption_2.png differ diff --git a/media/launch_configuration.png b/media/launch_configuration.png new file mode 100644 index 000000000..783531691 Binary files /dev/null and b/media/launch_configuration.png differ diff --git a/releng/com.espressif.idf.configuration/pom.xml b/releng/com.espressif.idf.configuration/pom.xml index aa37b3486..27472cd84 100644 --- a/releng/com.espressif.idf.configuration/pom.xml +++ b/releng/com.espressif.idf.configuration/pom.xml @@ -10,7 +10,7 @@ 4.0.12 ${tycho-version} UTF-8 - 3.6.0 + 3.7.0 diff --git a/releng/com.espressif.idf.product/idf.product b/releng/com.espressif.idf.product/idf.product index 6e861e8e0..1e4fc08f9 100644 --- a/releng/com.espressif.idf.product/idf.product +++ b/releng/com.espressif.idf.product/idf.product @@ -1,7 +1,7 @@ - + diff --git a/releng/com.espressif.idf.target/com.espressif.idf.target.target b/releng/com.espressif.idf.target/com.espressif.idf.target.target index a0d595a1d..cbe98dd3b 100644 --- a/releng/com.espressif.idf.target/com.espressif.idf.target.target +++ b/releng/com.espressif.idf.target/com.espressif.idf.target.target @@ -3,10 +3,9 @@ - + - @@ -16,17 +15,21 @@ - - + + + + + + @@ -45,7 +48,7 @@ - + @@ -78,7 +81,7 @@ - + @@ -87,7 +90,7 @@ - + @@ -96,7 +99,7 @@ - + diff --git a/tests/com.espressif.idf.core.test/.classpath b/tests/com.espressif.idf.core.test/.classpath index b5100a281..558597bd0 100644 --- a/tests/com.espressif.idf.core.test/.classpath +++ b/tests/com.espressif.idf.core.test/.classpath @@ -4,9 +4,10 @@ + - + diff --git a/tests/com.espressif.idf.core.test/.settings/org.eclipse.jdt.core.prefs b/tests/com.espressif.idf.core.test/.settings/org.eclipse.jdt.core.prefs index d089a9b73..7cbe36f26 100644 --- a/tests/com.espressif.idf.core.test/.settings/org.eclipse.jdt.core.prefs +++ b/tests/com.espressif.idf.core.test/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,11 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/tests/com.espressif.idf.core.test/META-INF/MANIFEST.MF b/tests/com.espressif.idf.core.test/META-INF/MANIFEST.MF index 2271a89ec..fb9f2b379 100644 --- a/tests/com.espressif.idf.core.test/META-INF/MANIFEST.MF +++ b/tests/com.espressif.idf.core.test/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-SymbolicName: com.espressif.idf.core.test Bundle-Version: 0.0.1.qualifier Bundle-Vendor: Espressif Systems Automatic-Module-Name: com.espressif.idf.core.test -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Require-Bundle: com.espressif.idf.core;bundle-version="1.0.1", junit-jupiter-api, org.eclipse.launchbar.core, diff --git a/tests/com.espressif.idf.ui.test/.classpath b/tests/com.espressif.idf.ui.test/.classpath index 56349ae0e..5f8db2475 100644 --- a/tests/com.espressif.idf.ui.test/.classpath +++ b/tests/com.espressif.idf.ui.test/.classpath @@ -1,9 +1,21 @@ - - - - + + + + + + + + + + + + + + + + diff --git a/tests/com.espressif.idf.ui.test/.settings/org.eclipse.jdt.core.prefs b/tests/com.espressif.idf.ui.test/.settings/org.eclipse.jdt.core.prefs index 7adc0fb9a..3a79233b1 100644 --- a/tests/com.espressif.idf.ui.test/.settings/org.eclipse.jdt.core.prefs +++ b/tests/com.espressif.idf.ui.test/.settings/org.eclipse.jdt.core.prefs @@ -1,10 +1,10 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.compiler.source=21 diff --git a/tests/com.espressif.idf.ui.test/META-INF/MANIFEST.MF b/tests/com.espressif.idf.ui.test/META-INF/MANIFEST.MF index 9ca7a0f58..206c0bd81 100644 --- a/tests/com.espressif.idf.ui.test/META-INF/MANIFEST.MF +++ b/tests/com.espressif.idf.ui.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Tests Bundle-SymbolicName: com.espressif.idf.ui.test;singleton:=true Bundle-Version: 0.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Automatic-Module-Name: com.espressif.idf.tests Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/common/utility/TestWidgetWaitUtility.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/common/utility/TestWidgetWaitUtility.java index f0144b80c..1cc1a4d5a 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/common/utility/TestWidgetWaitUtility.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/common/utility/TestWidgetWaitUtility.java @@ -277,7 +277,14 @@ public static void waitForCTabToAppear(SWTWorkbenchBot workbenchBot, String tabN @Override public boolean test() throws Exception { - return workbenchBot.cTabItem(tabName).isActive(); + try + { + return workbenchBot.cTabItem(tabName) != null; + } + catch (WidgetNotFoundException e) + { + return false; + } } @Override diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java index 02098858d..0ee44fbc3 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java @@ -20,7 +20,6 @@ import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException; import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; -import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.FixMethodOrder; @@ -76,7 +75,6 @@ public void creatingNewEspLaunchTarget() Fixture.givenTargetTypeSelectedIs("ESP Target"); Fixture.givenNewLaunchTargetName("TestESP"); Fixture.givenNewLaunchTargetComPortIsSetTo("COMTEST"); - Fixture.givenNewLaunchTargetIdfTargetSetTo("esp32s2"); Fixture.whenFinishIsClicked(); Fixture.thenLaunchTargetContains("TestESP"); Fixture.thenLaunchTargetIsSelectedFromLaunchTargets("TestESP"); @@ -119,7 +117,7 @@ private static void givenNewLaunchTargetIdfTargetSetTo(String espIdfTarget) private static void givenNewLaunchTargetComPortIsSetTo(String comPort) { - + if (bot.comboBoxWithLabel("Serial Port:").itemCount() != 0) { bot.comboBoxWithLabel("Serial Port:").setSelection(0); diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectJTAGFlashTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectJTAGFlashTest.java new file mode 100644 index 000000000..d06ef0d63 --- /dev/null +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectJTAGFlashTest.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. + * All rights reserved. Use is subject to license terms. + *******************************************************************************/ + +package com.espressif.idf.ui.test.executable.cases.project; + +import java.io.IOException; + +import org.apache.commons.lang3.SystemUtils; +import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; +import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import com.espressif.idf.ui.test.common.WorkBenchSWTBot; +import static org.eclipse.swtbot.swt.finder.waits.Conditions.*; +import static org.junit.Assert.assertTrue; + +import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; +import com.espressif.idf.ui.test.operations.EnvSetupOperations; +import com.espressif.idf.ui.test.operations.ProjectTestOperations; +import com.espressif.idf.ui.test.operations.selectors.LaunchBarConfigSelector; +import com.espressif.idf.ui.test.operations.selectors.LaunchBarTargetSelector; + +/** + * Test class to test JTAG Flash Process + * + * @author Andrii Filippov + * + */ + +@SuppressWarnings("restriction") +@RunWith(SWTBotJunit4ClassRunner.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class IDFProjectJTAGFlashTest +{ + @BeforeClass + public static void beforeTestClass() throws Exception + { + Fixture.loadEnv(); + } + + @After + public void afterEachTest() + { + try + { + Fixture.cleanTestEnv(); + } + catch (Exception e) + { + System.err.println("Error during cleanup: " + e.getMessage()); + } + } + + @Test + public void givenNewProjectCreatedWhenSelectJTAGflashWhenBuiltThenCheckFlashedSuccessfully() throws Exception + { + if (SystemUtils.IS_OS_LINUX) //temporary solution until new ESP boards arrive for Windows + { + Fixture.givenNewEspressifIDFProjectIsSelected("EspressIf", "Espressif IDF Project"); + Fixture.givenProjectNameIs("NewProjectJTAGFlashTest"); + Fixture.whenNewProjectIsSelected(); + Fixture.whenSelectJTAGflashInLaunchConfig(); + Fixture.whenSelectLaunchTargetBoard(); + Fixture.whenProjectIsBuiltUsingContextMenu(); + Fixture.whenFlashProject(); + Fixture.thenVerifyJTAGflashDone(); + } + else + { + assertTrue(true); + } + } + + private static class Fixture + { + private static SWTWorkbenchBot bot; + private static String category; + private static String subCategory; + private static String projectName; + + private static void loadEnv() throws Exception + { + bot = WorkBenchSWTBot.getBot(); + EnvSetupOperations.setupEspressifEnv(bot); + bot.sleep(1000); + ProjectTestOperations.deleteAllProjects(bot); + } + + private static void givenNewEspressifIDFProjectIsSelected(String category, String subCategory) + { + Fixture.category = category; + Fixture.subCategory = subCategory; + } + + private static void givenProjectNameIs(String projectName) + { + Fixture.projectName = projectName; + } + + private static void whenNewProjectIsSelected() throws Exception + { + ProjectTestOperations.setupProject(projectName, category, subCategory, bot); + } + + private static void whenProjectIsBuiltUsingContextMenu() throws IOException + { + ProjectTestOperations.buildProjectUsingContextMenu(projectName, bot); + ProjectTestOperations.waitForProjectBuild(bot); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + } + + private static void whenFlashProject() throws IOException + { + ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Run Configurations..."); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "Run Configurations", 10000); + bot.tree().getTreeItem("ESP-IDF Application").select(); + bot.tree().getTreeItem("ESP-IDF Application").expand(); + bot.tree().getTreeItem("ESP-IDF Application").getNode(projectName).select(); + bot.waitUntil(widgetIsEnabled(bot.button("Run")), 5000); + bot.button("Run").click(); + } + + private static void whenSelectJTAGflashInLaunchConfig() throws Exception + { + LaunchBarConfigSelector configSelector = new LaunchBarConfigSelector(bot); + configSelector.clickEdit(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "Edit Configuration", 20000); + bot.cTabItem("Main").show(); + bot.cTabItem("Main").setFocus(); + bot.comboBoxWithLabel("Flash over:").setSelection("JTAG"); + bot.button("OK").click(); + } + + private static void whenSelectLaunchTargetBoard() throws Exception + { + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.clickEdit(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "New ESP Target", 20000); + SWTBotShell shell = bot.shell("New ESP Target"); + bot.comboBoxWithLabel("Board:").setSelection("ESP32-ETHERNET-KIT [usb://1-10]"); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + shell.setFocus(); + bot.button("Finish").click(); + } + + private static void thenVerifyJTAGflashDone() throws Exception + { + ProjectTestOperations.verifyTheConsoleOutput(bot, "** Flashing done for partition_table/partition-table.bin"); + } + + private static void cleanTestEnv() + { + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + ProjectTestOperations.closeAllProjects(bot); + ProjectTestOperations.deleteAllProjects(bot); + } + } +} diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLaunchTargetEditorFunctionalityTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLaunchTargetEditorFunctionalityTest.java new file mode 100644 index 000000000..4d40ef4ce --- /dev/null +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLaunchTargetEditorFunctionalityTest.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.ui.test.executable.cases.project; + +import static org.eclipse.swtbot.swt.finder.waits.Conditions.widgetIsEnabled; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; +import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import com.espressif.idf.ui.handlers.Messages; +import com.espressif.idf.ui.test.common.WorkBenchSWTBot; +import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; +import com.espressif.idf.ui.test.operations.EnvSetupOperations; +import com.espressif.idf.ui.test.operations.ProjectTestOperations; +import com.espressif.idf.ui.test.operations.selectors.LaunchBarTargetSelector; + +/** + * Test class to test the Launch Target Editor + * + * @author Andrii Filippov + * + */ +@SuppressWarnings("restriction") +@RunWith(SWTBotJunit4ClassRunner.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) + +public class IDFProjectLaunchTargetEditorFunctionalityTest { + @BeforeClass + public static void beforeTestClass() throws Exception + { + Fixture.loadEnv(); + Fixture.givenNewEspressifIDFProjectIsSelected("EspressIf", "Espressif IDF Project"); + Fixture.givenProjectNameIs("LaunchTargetEditorTest"); + Fixture.whenNewProjectIsSelected(); + } + + @AfterClass + public static void afterEachTest() + { + try + { + Fixture.cleanTestEnv(); + } + catch (Exception e) + { + System.err.println("Error during cleanup: " + e.getMessage()); + } + } + + @Test + public void givenANewProjectCreatedBuiltWhenSelectNewTargetWhenPopUpAppearsThenBuildFolderDeletedSuccessfully() + throws Exception + { + Fixture.whenProjectIsBuiltUsingContextMenu(); + Fixture.whenChangeLaunchTarget(); + Fixture.whenRefreshProject(); + Fixture.thenBuildFolderDeletedSuccessfully(); + } + + @Test + public void givenBNewProjectCreatedWhenCreateNewLaunchTargetThenProjectBuiltSuccessfully() + throws Exception + { + Fixture.whenCreateNewLaunchTarget(); + Fixture.whenProjectIsBuiltUsingContextMenu(); + } + + @Test + public void givenCNewProjectCreatedWhenDeleteSelectedLaunchTargetThenDeletedSuccessfully() + throws Exception + { + Fixture.whenProjectFullCleanUsingContextMenu(); + Fixture.whenDeleteSelectedLaunchTarget(); + Fixture.thenLaunchTargetDeletedSuccessfully(); + } + + private static class Fixture + { + private static SWTWorkbenchBot bot; + private static String category; + private static String subCategory; + private static String projectName; + + private static void loadEnv() throws Exception + { + bot = WorkBenchSWTBot.getBot(); + EnvSetupOperations.setupEspressifEnv(bot); + bot.sleep(1000); + ProjectTestOperations.deleteAllProjects(bot); + } + + private static void givenNewEspressifIDFProjectIsSelected(String category, String subCategory) + { + Fixture.category = category; + Fixture.subCategory = subCategory; + } + + private static void givenProjectNameIs(String projectName) + { + Fixture.projectName = projectName; + } + + private static void whenNewProjectIsSelected() throws Exception + { + ProjectTestOperations.setupProject(projectName, category, subCategory, bot); + } + + private static void whenProjectIsBuiltUsingContextMenu() throws IOException + { + ProjectTestOperations.buildProjectUsingContextMenu(projectName, bot); + ProjectTestOperations.waitForProjectBuild(bot); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + } + + private static void whenChangeLaunchTarget() throws Exception + { + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.selectTarget("esp32c2"); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "IDF Launch Target Changed", 20000); + SWTBotShell shell = bot.shell("IDF Launch Target Changed"); + shell.setFocus(); + bot.button("Yes").click(); + } + + private static void whenSelectLaunchTarget() throws Exception + { + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.selectTarget("target"); + } + + private static void whenDeleteLaunchTarget() throws Exception + { + bot.sleep(500); + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.clickEdit(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "New ESP Target", 20000); + SWTBotShell shell = bot.shell("New ESP Target"); + shell.setFocus(); + bot.button("Delete").click(); + } + + private static void whenDeleteSelectedLaunchTarget() throws Exception + { + whenSelectLaunchTarget(); + whenDeleteLaunchTarget(); + } + + private static void thenLaunchTargetDeletedSuccessfully() throws Exception + { + bot.sleep(500); + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + assertTrue("Launch Target was not deleted successfully!", !targetSelector.isTargetPresent("target")); + } + + private static void selectNewLaunchTarget() + { + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.select("New Launch Target..."); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "New Launch Target", 20000); + assertTrue("'New Launch Target' dialog did not appear", bot.shell("New Launch Target").isActive()); + } + + private static void handleNewLaunchTargetDialog() throws Exception + { + SWTBotShell shell = bot.shell("New Launch Target"); + bot.table().select("ESP Target"); + shell.setFocus(); + bot.waitUntil(widgetIsEnabled(bot.button("Next >")), 5000); + bot.button("Next >").click(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "New ESP Target",10000); + } + + private static void handleNewEspTargetDialog() throws Exception + { + bot.textWithLabel("Name:").setText("target"); + bot.button("Finish").click(); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + } + + private static void whenCreateNewLaunchTarget() throws Exception + { + selectNewLaunchTarget(); + handleNewLaunchTargetDialog(); + handleNewEspTargetDialog(); + } + + private static void whenRefreshProject() throws IOException + { + ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Refresh"); + } + + private static void thenBuildFolderDeletedSuccessfully() throws Exception + { + assertTrue("Build folder was not deleted successfully!", ProjectTestOperations.findProjectFullCleanedFilesInBuildFolder(projectName, bot)); + } + + private static void whenProjectFullCleanUsingContextMenu() throws IOException + { + ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Project Full Clean"); + ProjectTestOperations.joinJobByName(Messages.ProjectFullCleanCommandHandler_RunningFullcleanJobName); + ProjectTestOperations.findInConsole(bot, "Espressif IDF Tools Console", "Done"); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + } + + private static void cleanTestEnv() + { + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + ProjectTestOperations.closeAllProjects(bot); + ProjectTestOperations.deleteAllProjects(bot); + } + } +} + diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLspCdtFunctionalityTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLspCdtFunctionalityTest.java index 917894f01..cab910a29 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLspCdtFunctionalityTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/IDFProjectLspCdtFunctionalityTest.java @@ -9,8 +9,6 @@ import java.util.List; -import org.eclipse.jface.bindings.keys.KeyStroke; -import org.eclipse.jface.bindings.keys.ParseException; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; @@ -50,9 +48,10 @@ public static void setupEnv() throws Exception bot = WorkBenchSWTBot.getBot(); EnvSetupOperations.setupEspressifEnv(bot); ProjectTestOperations.deleteAllProjects(bot); - ProjectTestOperations.setupProjectWithReconfigureCommand(PROJECT_NAME, "EspressIf", "Espressif IDF Project", //$NON-NLS-1$ //$NON-NLS-2$ - bot); - TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + ProjectTestOperations.setupProject(PROJECT_NAME, "EspressIf", "Espressif IDF Project", bot); + ProjectTestOperations.buildProjectUsingContextMenu(PROJECT_NAME, bot); + ProjectTestOperations.waitForProjectBuild(bot); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); } @AfterClass @@ -79,24 +78,25 @@ public void test_ContentAssist_ProposalsAppear() assertTrue("Expected LSP proposals not found", !proposals.isEmpty()); //$NON-NLS-1$ } - @Test - public void test_HoverInformationAppears() throws ParseException - { - openMainCFile(); - SWTBotEditor editor = bot.editorByTitle("main.c"); //$NON-NLS-1$ - editor.setFocus(); - TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); - - bot.sleep(2000); - editor.toTextEditor().navigateTo(0, 5); - editor.toTextEditor().pressShortcut(KeyStroke.getInstance("F2")); //$NON-NLS-1$ - bot.sleep(2000); - - // lsp hover contains the code field - boolean hoverShown = editor.toTextEditor().bot().shell("").bot().browser().getText().contains("code"); //$NON-NLS-1$ //$NON-NLS-2$ - - assertTrue("Hover documentation not shown", hoverShown); //$NON-NLS-1$ - } + // TODO: This test is flaky — it occasionally fails on CI runners and sometimes locally when run with other tests. +// @Test +// public void test_HoverInformationAppears() throws ParseException +// { +// openMainCFile(); +// SWTBotEditor editor = bot.editorByTitle("main.c"); //$NON-NLS-1$ +// editor.setFocus(); +// TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); +// +// bot.sleep(2000); +// editor.toTextEditor().navigateTo(0, 5); +// editor.toTextEditor().pressShortcut(KeyStroke.getInstance("F2")); //$NON-NLS-1$ +// bot.sleep(2000); +// +// // lsp hover contains the code field +// boolean hoverShown = editor.toTextEditor().bot().shell("").bot().browser().getText().contains("code"); //$NON-NLS-1$ //$NON-NLS-2$ +// +// assertTrue("Hover documentation not shown", hoverShown); //$NON-NLS-1$ +// } @Test public void test_Diagnostics_NoErrorThenErrorAppears() diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectClangFilesTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectClangFilesTest.java index 32e048cf2..8bfaefb13 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectClangFilesTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectClangFilesTest.java @@ -4,9 +4,9 @@ *******************************************************************************/ package com.espressif.idf.ui.test.executable.cases.project; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,7 +14,6 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; @@ -28,13 +27,11 @@ import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; -import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.ui.test.common.WorkBenchSWTBot; import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; import com.espressif.idf.ui.test.operations.EnvSetupOperations; import com.espressif.idf.ui.test.operations.ProjectTestOperations; -import com.espressif.idf.ui.test.operations.selectors.LaunchBarConfigSelector; import com.espressif.idf.ui.test.operations.selectors.LaunchBarTargetSelector; /** @@ -116,7 +113,8 @@ public void givenNewProjectIsCreatedThenTestClangFormatFileSettingsAreBeingAppli } @Test - public void givenNewProjectsAreCreatedAndBuiltWhenPreferencesOpenedThenClangdArgumentMatchesExpected() throws Exception + public void givenNewProjectsAreCreatedAndBuiltWhenPreferencesOpenedThenClangdArgumentMatchesExpected() + throws Exception { Fixture.givenNewProjectIsCreated("NewProjectClangFilesTest4"); Fixture.whenProjectIsBuiltUsingContextMenu("NewProjectClangFilesTest4"); @@ -127,7 +125,8 @@ public void givenNewProjectsAreCreatedAndBuiltWhenPreferencesOpenedThenClangdArg } @Test - public void givenNewProjectIsCreatedWhenBuiltThenClangdDriversUpdateOnTargetWhenChangeTargetAndBuiltThenClangdDriversUpdateOnTargetChange() throws Exception + public void givenNewProjectIsCreatedWhenBuiltThenClangdDriversUpdateOnTargetWhenChangeTargetAndBuiltThenClangdDriversUpdateOnTargetChange() + throws Exception { Fixture.givenNewProjectIsCreated("NewProjectClangFilesTest6"); Fixture.whenProjectIsBuiltUsingContextMenu("NewProjectClangFilesTest6"); @@ -186,7 +185,7 @@ private static void thenCheckClangdArgumentAfterProjectBuilt(String projectName) Fixture.closePreferencesDialog(); } - private static void whenNewProjectIsSelected( String projectName) throws Exception + private static void whenNewProjectIsSelected(String projectName) throws Exception { ProjectTestOperations.setupProject(projectName, category, subCategory, bot); TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); @@ -255,14 +254,17 @@ private static void whenClangFormatFileOpenedUsingDoubleClick() throws IOExcepti private static String getExpectedBuildFolderPATH(String projectName) throws IOException { - try { - IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); - String buildFolder = IDFUtil.getBuildDir(project); - Path buildFolderPath = Paths.get(buildFolder); - return buildFolderPath.toAbsolutePath().toString(); - } catch (CoreException e) { - throw new IOException("Failed to get build directory for project: " + projectName, e); - } + try + { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + String buildFolder = IDFUtil.getBuildDir(project); + Path buildFolderPath = Paths.get(buildFolder); + return buildFolderPath.toAbsolutePath().toString(); + } + catch (CoreException e) + { + throw new IOException("Failed to get build directory for project: " + projectName, e); + } } private static void thenClangdFileContentChecked() throws Exception @@ -290,31 +292,32 @@ private static void thenClangFormatContentChecked() throws Exception bot.cTabItem(".clang-format").activate(); assertTrue(ProjectTestOperations.checkExactMatchInTextEditor( """ - # We'll use defaults from the LLVM style, but with some modifications so that it's close to the CDT K&R style. - BasedOnStyle: LLVM - UseTab: Always - IndentWidth: 4 - TabWidth: 4 - BreakConstructorInitializers: AfterColon - IndentAccessModifiers: false - AccessModifierOffset: -4 - """, + # We'll use defaults from the LLVM style, but with some modifications so that it's close to the CDT K&R style. + BasedOnStyle: LLVM + UseTab: Always + IndentWidth: 4 + TabWidth: 4 + BreakConstructorInitializers: AfterColon + IndentAccessModifiers: false + AccessModifierOffset: -4 + """, bot)); } private static void thenClangFormatContentEdited() throws Exception { SWTBotEditor textEditor = bot.activeEditor(); - textEditor.toTextEditor().setText(""" - # We'll use defaults from the LLVM style, but with some modifications so that it's close to the CDT K&R style. - BasedOnStyle: LLVM - UseTab: Always - IndentWidth: 0 - TabWidth: 0 - BreakConstructorInitializers: AfterColon - IndentAccessModifiers: false - AccessModifierOffset: -4 - """); + textEditor.toTextEditor().setText( + """ + # We'll use defaults from the LLVM style, but with some modifications so that it's close to the CDT K&R style. + BasedOnStyle: LLVM + UseTab: Always + IndentWidth: 0 + TabWidth: 0 + BreakConstructorInitializers: AfterColon + IndentAccessModifiers: false + AccessModifierOffset: -4 + """); } private static void addSpaceToMainFile() throws Exception @@ -353,7 +356,7 @@ void app_main(void) { private static void setupAutoSave() throws Exception { - bot.menu("Window").menu("Preferences").click(); + bot.menu("Window").menu("Preferences...").click(); SWTBotShell prefrencesShell = bot.shell("Preferences"); prefrencesShell.bot().tree().getTreeItem("C/C++").select(); prefrencesShell.bot().tree().getTreeItem("C/C++").expand(); @@ -391,39 +394,43 @@ private static void thenMainFileShellClosed() throws IOException bot.shell("Save Resource").bot().button("Save").click(); } - private static String getExpectedBuildFolderPATHforClangdAdditionalArgument(String projectName) throws IOException + private static String getExpectedBuildFolderPATHforClangdAdditionalArgument(String projectName) + throws IOException { - try { - IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); - String buildFolder = IDFUtil.getBuildDir(project); - Path buildFolderPath = Paths.get(buildFolder); - return "--compile-commands-dir=" + buildFolderPath.toAbsolutePath().toString(); - } catch (CoreException e) { - throw new IOException("Failed to get build directory for project: " + projectName, e); - } + try + { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + String buildFolder = IDFUtil.getBuildDir(project); + Path buildFolderPath = Paths.get(buildFolder); + return "--compile-commands-dir=" + buildFolderPath.toAbsolutePath().toString(); + } + catch (CoreException e) + { + throw new IOException("Failed to get build directory for project: " + projectName, e); + } } private static String getXtensaToolchainPathBasedOnTheTargetConfigured() throws IOException { - String toolchain = IDFUtil.getToolchainExePathForActiveTarget(); + String toolchain = IDFUtil.getToolchainExePathForActiveTarget(); Path toolchainPath = Paths.get(toolchain); return toolchainPath.toAbsolutePath().toString(); } private static void thenCompareActualClangdArgumentWithExpected(String projectName) throws IOException - { - SWTBotShell prefrencesShell = bot.shell("Preferences"); - String actualClangdPath = prefrencesShell.bot().textWithLabel("Additional").getText(); - String expectedClangdPath = getExpectedBuildFolderPATHforClangdAdditionalArgument(projectName); - assertTrue(expectedClangdPath.equals(actualClangdPath)); + { + SWTBotShell prefrencesShell = bot.shell("Preferences"); + String actualClangdPath = prefrencesShell.bot().textWithLabel("Additional").getText(); + String expectedClangdPath = getExpectedBuildFolderPATHforClangdAdditionalArgument(projectName); + assertTrue(expectedClangdPath.equals(actualClangdPath)); } private static void thenCompareActualClangdDriversWithExpected(String projectName) throws IOException - { - SWTBotShell prefrencesShell = bot.shell("Preferences"); - String actualClangdPath = prefrencesShell.bot().textWithLabel("Drivers").getText(); - String expectedClangdPath = getXtensaToolchainPathBasedOnTheTargetConfigured(); - assertTrue(expectedClangdPath.equals(actualClangdPath)); + { + SWTBotShell prefrencesShell = bot.shell("Preferences"); + String actualDriversPath = prefrencesShell.bot().textWithLabel("Drivers").getText(); + String expectedDriversPath = "**"; + assertEquals(expectedDriversPath, actualDriversPath); } private static void whenProjectIsBuiltUsingContextMenu(String projectName) throws IOException @@ -434,7 +441,7 @@ private static void whenProjectIsBuiltUsingContextMenu(String projectName) throw private static void whenOpenClangdPreferences() throws Exception { - bot.menu("Window").menu("Preferences").click(); + bot.menu("Window").menu("Preferences...").click(); TestWidgetWaitUtility.waitForDialogToAppear(bot, "Preferences", 10000); SWTBotShell prefrencesShell = bot.shell("Preferences"); prefrencesShell.bot().tree().getTreeItem("C/C++").select(); @@ -444,17 +451,19 @@ private static void whenOpenClangdPreferences() throws Exception prefrencesShell.bot().tree().getTreeItem("C/C++").getNode("Editor (LSP)").getNode("clangd").select(); } - private static void closePreferencesDialog() { - SWTBotShell preferencesShell = bot.shell("Preferences"); - preferencesShell.bot().button("Cancel").click(); // Or "Apply and Close" - TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "Preferences", 10000); + private static void closePreferencesDialog() + { + SWTBotShell preferencesShell = bot.shell("Preferences"); + preferencesShell.bot().button("Cancel").click(); // Or "Apply and Close" + TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "Preferences", 10000); } - private static void closeTargetDialog() { + private static void closeTargetDialog() + { TestWidgetWaitUtility.waitForDialogToAppear(bot, "IDF Launch Target Changed", 10000); - SWTBotShell preferencesShell = bot.shell("IDF Launch Target Changed"); - preferencesShell.bot().button("Yes").click(); // Or "Apply and Close" - TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "IDF Launch Target Changed", 10000); + SWTBotShell preferencesShell = bot.shell("IDF Launch Target Changed"); + preferencesShell.bot().button("Yes").click(); // Or "Apply and Close" + TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "IDF Launch Target Changed", 10000); } private static void cleanTestEnv() diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectFlashProcessTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectFlashProcessTest.java new file mode 100644 index 000000000..a916828dd --- /dev/null +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectFlashProcessTest.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright 2021 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.ui.test.executable.cases.project; + +import java.io.IOException; + +import org.apache.commons.lang3.SystemUtils; +import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; +import static org.eclipse.swtbot.swt.finder.waits.Conditions.*; +import static org.junit.Assert.assertTrue; + +import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import com.espressif.idf.ui.test.common.WorkBenchSWTBot; +import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; +import com.espressif.idf.ui.test.operations.EnvSetupOperations; +import com.espressif.idf.ui.test.operations.ProjectTestOperations; +import com.espressif.idf.ui.test.operations.selectors.LaunchBarConfigSelector; +import com.espressif.idf.ui.test.operations.selectors.LaunchBarTargetSelector; + +/** + * Test class to test the Flash process + * + * @author Andrii Filippov + * + */ +@SuppressWarnings("restriction") +@RunWith(SWTBotJunit4ClassRunner.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) + +public class NewEspressifIDFProjectFlashProcessTest { + @BeforeClass + public static void beforeTestClass() throws Exception + { + Fixture.loadEnv(); + } + + @After + public void afterEachTest() + { + try + { + Fixture.cleanTestEnv(); // Make sure test environment is always cleaned up + } + catch (Exception e) + { + System.err.println("Error during cleanup: " + e.getMessage()); + } + } + + + @Test + public void givenNewProjectCreatedBuiltWhenSelectSerialPortWhenFlashThenCheckFlashedSuccessfully() + throws Exception + { + if (SystemUtils.IS_OS_LINUX) //temporary solution until new ESP boards arrive for Windows + { + Fixture.givenNewEspressifIDFProjectIsSelected("EspressIf", "Espressif IDF Project"); + Fixture.givenProjectNameIs("NewProjectFlashTest"); + Fixture.whenNewProjectIsSelected(); + Fixture.whenTurnOffOpenSerialMonitorAfterFlashingInLaunchConfig(); + Fixture.whenProjectIsBuiltUsingContextMenu(); + Fixture.whenSelectLaunchTargetSerialPort(); + Fixture.whenFlashProject(); + Fixture.thenVerifyFlashDoneSuccessfully(); + } + else + { + assertTrue(true); + } + } + + private static class Fixture + { + private static SWTWorkbenchBot bot; + private static String category; + private static String subCategory; + private static String projectName; + + private static void loadEnv() throws Exception + { + bot = WorkBenchSWTBot.getBot(); + EnvSetupOperations.setupEspressifEnv(bot); + bot.sleep(1000); + ProjectTestOperations.deleteAllProjects(bot); + } + + private static void givenNewEspressifIDFProjectIsSelected(String category, String subCategory) + { + Fixture.category = category; + Fixture.subCategory = subCategory; + } + + private static void givenProjectNameIs(String projectName) + { + Fixture.projectName = projectName; + } + + private static void whenNewProjectIsSelected() throws Exception + { + ProjectTestOperations.setupProject(projectName, category, subCategory, bot); + } + + private static void whenProjectIsBuiltUsingContextMenu() throws IOException + { + ProjectTestOperations.buildProjectUsingContextMenu(projectName, bot); + ProjectTestOperations.waitForProjectBuild(bot); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + } + + private static void cleanTestEnv() + { + TestWidgetWaitUtility.waitForOperationsInProgressToFinishAsync(bot); + ProjectTestOperations.closeAllProjects(bot); + ProjectTestOperations.deleteAllProjects(bot); + } + + private static void whenSelectLaunchTargetSerialPort() throws Exception + { + LaunchBarTargetSelector targetSelector = new LaunchBarTargetSelector(bot); + targetSelector.clickEdit(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "New ESP Target", 20000); + SWTBotShell shell = bot.shell("New ESP Target"); + bot.comboBoxWithLabel("Serial Port:").setSelection("/dev/ttyUSB1 Dual RS232-HS"); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + shell.setFocus(); + bot.button("Finish").click(); + } + + private static void whenTurnOffOpenSerialMonitorAfterFlashingInLaunchConfig() throws Exception + { + LaunchBarConfigSelector configSelector = new LaunchBarConfigSelector(bot); + configSelector.clickEdit(); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "Edit Configuration", 20000); + bot.cTabItem("Main").show(); + bot.cTabItem("Main").setFocus(); + SWTBotCheckBox checkBox = bot.checkBox("Open Serial Monitor After Flashing"); + if (checkBox.isChecked()) { + checkBox.click(); + } + bot.button("OK").click(); + } + + private static void whenFlashProject() throws IOException + { + ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Run Configurations..."); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "Run Configurations", 10000); + bot.tree().getTreeItem("ESP-IDF Application").select(); + bot.tree().getTreeItem("ESP-IDF Application").expand(); + bot.tree().getTreeItem("ESP-IDF Application").getNode(projectName).select(); + bot.waitUntil(widgetIsEnabled(bot.button("Run")), 5000); + bot.button("Run").click(); + } + + private static void thenVerifyFlashDoneSuccessfully() throws Exception + { + ProjectTestOperations.waitForProjectFlash(bot); + } + } +} diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectSDKconfigTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectSDKconfigTest.java index 0d3251443..ed891fbd6 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectSDKconfigTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectSDKconfigTest.java @@ -172,26 +172,26 @@ private static void whenProjectIsBuiltUsingContextMenu() throws IOException private static void thenSDKconfigFileIsPresent() throws IOException { - assertTrue(bot.tree().getTreeItem(projectName).getNode("sdkconfig") != null); + assertTrue("SDKconfig file was not found", bot.tree().getTreeItem(projectName).getNode("sdkconfig") != null); } private static void thenSDKconfigFileIsAbsent() throws IOException { - assertTrue(!bot.tree().getTreeItem(projectName).getNodes().contains("sdkconfig")); + assertTrue("SDKconfig file is still present",!bot.tree().getTreeItem(projectName).getNodes().contains("sdkconfig")); } private static void whenSDKconfigFileOpenedUsingDoubleClick() throws IOException { bot.tree().getTreeItem(projectName).getNode("sdkconfig").doubleClick(); TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "Progress Information", 40000); - TestWidgetWaitUtility.waitForCTabToAppear(bot, "SDK Configuration (sdkconfig)", 5000); + TestWidgetWaitUtility.waitForCTabToAppear(bot, "SDK Configuration (sdkconfig)", 10000); } private static void whenSDKconfigFileOpenedUsingContextMenu() throws IOException { ProjectTestOperations.launchCommandUsingContextMenu(projectName, bot, "Menu Config"); TestWidgetWaitUtility.waitWhileDialogIsVisible(bot, "Progress Information", 40000); - TestWidgetWaitUtility.waitForCTabToAppear(bot, "SDK Configuration (sdkconfig)", 5000); + TestWidgetWaitUtility.waitForCTabToAppear(bot, "SDK Configuration (sdkconfig)", 10000); } private static void thenSDKconfigFileContentChecked() throws Exception @@ -200,7 +200,7 @@ private static void thenSDKconfigFileContentChecked() throws Exception TestWidgetWaitUtility.waitForTreeItem("Partition Table", bot.tree(1), bot); bot.tree(1).getTreeItem("Partition Table").click(); bot.sleep(1000); - assertTrue(bot.textWithLabel("Offset of partition table (hex)").getText().matches("0x8000")); + assertTrue("'Offset of partition table (hex)' does not match '0x8000'", bot.textWithLabel("Offset of partition table (hex)").getText().matches("0x8000")); } private static void thenSDKconfigFileContentEdited() throws Exception @@ -210,9 +210,9 @@ private static void thenSDKconfigFileContentEdited() throws Exception bot.tree(1).getTreeItem("Partition Table").click(); bot.sleep(1000); bot.textWithLabel("Offset of partition table (hex)").setText("0x4000"); - bot.sleep(2000); + bot.sleep(1000); bot.comboBoxWithLabel("Partition Table").setSelection("Custom partition table CSV"); - bot.sleep(2000); + bot.sleep(1000); bot.checkBox("Generate an MD5 checksum for the partition table").click(); } @@ -225,8 +225,7 @@ private static void whenSDKconfigFileIsSaved() throws IOException { bot.cTabItem("*SDK Configuration (sdkconfig)").activate(); bot.cTabItem("*SDK Configuration (sdkconfig)").close(); - bot.sleep(5000); - TestWidgetWaitUtility.waitForDialogToAppear(bot, "Save Resource", 5000); + TestWidgetWaitUtility.waitForDialogToAppear(bot, "Save Resource", 10000); bot.shell("Save Resource").bot().button("Save").click(); } @@ -235,12 +234,12 @@ private static void thenCheckChangesAreSaved() throws Exception bot.cTabItem("SDK Configuration (sdkconfig)").activate(); TestWidgetWaitUtility.waitForTreeItem("Partition Table", bot.tree(1), bot); bot.tree(1).getTreeItem("Partition Table").click(); - bot.sleep(2000); - assertTrue(bot.textWithLabel("Offset of partition table (hex)").getText().matches("0x4000")); - bot.sleep(2000); - assertTrue(bot.comboBoxWithLabel("Partition Table").selection().equals("Custom partition table CSV")); - bot.sleep(2000); - assertTrue(!bot.checkBox("Generate an MD5 checksum for the partition table").isChecked()); + bot.sleep(1000); + assertTrue("'Offset of partition table (hex)' does not match '0x4000'", bot.textWithLabel("Offset of partition table (hex)").getText().matches("0x4000")); + bot.sleep(1000); + assertTrue("'Partition Table' does not match 'Custom partition table CSV'", bot.comboBoxWithLabel("Partition Table").selection().equals("Custom partition table CSV")); + bot.sleep(1000); + assertTrue("'Generate an MD5 checksum for the partition table' checkbox is still checked!", !bot.checkBox("Generate an MD5 checksum for the partition table").isChecked()); } private static void cleanTestEnv() diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectTest.java index c44048d93..3c08982d6 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/project/NewEspressifIDFProjectTest.java @@ -424,13 +424,8 @@ public static void openMainCAndCheckIfNoExceptionInErrorLog() throws Exception editor.setFocus(); TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); - // Open the "Error Log" view - bot.menu("Window").menu("Show View").menu("Other...").click(); - bot.tree().getTreeItem("General").select(); - bot.tree().getTreeItem("General").doubleClick(); - bot.tree().getTreeItem("General").getNode("Error Log").select(); - bot.button("Open").click(); - SWTBotView errorLogView = bot.activeView(); + SWTBotView errorLogView = bot.viewByPartName("Problems"); + errorLogView.show(); try { diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/EnvSetupOperations.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/EnvSetupOperations.java index ce94217ef..1f165485c 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/EnvSetupOperations.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/EnvSetupOperations.java @@ -2,9 +2,12 @@ import static org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory.withPartName; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import org.eclipse.ui.PlatformUI; import com.espressif.idf.ui.test.common.configs.DefaultPropertyFetcher; import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; @@ -23,6 +26,19 @@ public static void setupEspressifEnv(SWTWorkbenchBot bot) throws Exception if (SETUP) return; + Display.getDefault().syncExec(() -> { + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + // Maximize (Windows/Linux/macOS windowed) + shell.setMaximized(true); + try + { + shell.setFullScreen(true); + } + catch (Throwable ignore) + { + } + }); + for (SWTBotView view : bot.views(withPartName("Welcome"))) { view.close(); @@ -31,7 +47,7 @@ public static void setupEspressifEnv(SWTWorkbenchBot bot) throws Exception bot.table().select("C/C++"); bot.button("Open").click(); - bot.menu("Window").menu("Preferences").click(); + bot.menu("Window").menu("Preferences...").click(); SWTBotShell prefrencesShell = bot.shell("Preferences"); prefrencesShell.bot().tree().getTreeItem("General").select(); diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/ProjectTestOperations.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/ProjectTestOperations.java index 1012f68e9..2e4cff321 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/ProjectTestOperations.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/ProjectTestOperations.java @@ -52,6 +52,8 @@ public class ProjectTestOperations { private static final String DEFAULT_PROJECT_BUILD_WAIT_PROPERTY = "default.project.build.wait"; + + private static final String DEFAULT_FLASH_WAIT_PROPERTY = "default.project.flash.wait"; private static final Logger logger = LoggerFactory.getLogger(ProjectTestOperations.class); @@ -86,8 +88,20 @@ public static void waitForProjectBuild(SWTWorkbenchBot bot) throws IOException SWTBotView consoleView = viewConsole("CDT Build Console", bot); consoleView.show(); consoleView.setFocus(); + try { TestWidgetWaitUtility.waitUntilViewContains(bot, "Build complete", consoleView, DefaultPropertyFetcher.getLongPropertyValue(DEFAULT_PROJECT_BUILD_WAIT_PROPERTY, 300000)); + } catch (Exception e) { + throw new AssertionError("Project Build failed", e); + } + } + + public static void waitForProjectFlash(SWTWorkbenchBot bot) throws IOException + { + SWTBotView view = bot.viewByPartName("Console"); + view.setFocus(); + TestWidgetWaitUtility.waitUntilViewContains(bot, "Hard resetting via RTS pin...", view, + DefaultPropertyFetcher.getLongPropertyValue(DEFAULT_FLASH_WAIT_PROPERTY, 120000)); } public static void waitForProjectNewComponentInstalled(SWTWorkbenchBot bot) throws IOException @@ -745,6 +759,15 @@ public static void deletePartitionTableRow(SWTWorkbenchBot bot) throws IOExcepti bot.button("OK").click(); } + + public static void verifyTheConsoleOutput(SWTWorkbenchBot bot, String text) throws IOException + { + SWTBotView view = bot.viewByPartName("Console"); + view.setFocus(); + TestWidgetWaitUtility.waitUntilViewContains(bot, text, view, + DefaultPropertyFetcher.getLongPropertyValue(DEFAULT_FLASH_WAIT_PROPERTY, 120000)); + } + public static void joinJobByName(String jobName) { Job[] jobs = Job.getJobManager().find(null); diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/selectors/LaunchBarTargetSelector.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/selectors/LaunchBarTargetSelector.java index 09fdcda25..6bc2938ab 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/selectors/LaunchBarTargetSelector.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/operations/selectors/LaunchBarTargetSelector.java @@ -8,6 +8,8 @@ import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withText; +import java.util.List; + import org.eclipse.launchbar.ui.controls.internal.CSelector; import org.eclipse.launchbar.ui.controls.internal.LaunchBarWidgetIds; import org.eclipse.launchbar.ui.controls.internal.TargetSelector; @@ -23,7 +25,9 @@ import org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory; import org.eclipse.swtbot.swt.finder.results.Result; import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBotControl; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotLabel; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.allOf; /** * Launchbar CDT helper class to select items from launch targets @@ -68,10 +72,9 @@ public void click(int x, int y) notify(SWT.MouseUp, createMouseEvent(x, y, 1, SWT.BUTTON1, 1)); } - public void clickEdit() + public void clickEdit() { - click(); - bot().buttonWithId(LaunchBarWidgetIds.EDIT).click(); // $NON-NLS-1$ + bot().canvasWithId(LaunchBarWidgetIds.EDIT).click(); // $NON-NLS-1$ } private void clickOnInternalWidget(int x, int y, Widget internalWidget) @@ -99,27 +102,80 @@ public LaunchBarTargetSelector select(String text) public LaunchBarTargetSelector selectTarget(String text) { - click(); - SWTBotShell swtBotShell = bot().shellWithId(LaunchBarWidgetIds.POPUP); - ScrolledComposite scrolledComposite = swtBotShell.bot().widget(widgetOfType(ScrolledComposite.class)); - int numberOfItemsInScrolledComp = syncExec( - () -> ((Composite) scrolledComposite.getChildren()[0]).getChildren().length); - Label itemToSelect; - - // Set the text in the not visible text field - // when the target list is too big, swtbot cannot select a target label, so we filter the list - if (numberOfItemsInScrolledComp > NUM_FOR_FILTER_POPUP) - { - swtBotShell.bot().text().setText(text); - itemToSelect = swtBotShell.bot().label(0).widget; - } - else - { - itemToSelect = swtBotShell.bot().widget(withText(text)); - } - - Point itemToSelectLocation = syncExec((Result) itemToSelect::getLocation); - clickOnInternalWidget(itemToSelectLocation.x, itemToSelectLocation.y, itemToSelect); - return this; + click(); + SWTBotShell swtBotShell = bot().shellWithId(LaunchBarWidgetIds.POPUP); + ScrolledComposite scrolledComposite = swtBotShell.bot().widget(widgetOfType(ScrolledComposite.class)); + int numberOfItemsInScrolledComp = syncExec( + () -> ((Composite) scrolledComposite.getChildren()[0]).getChildren().length); + Label itemToSelect; + + if (numberOfItemsInScrolledComp > NUM_FOR_FILTER_POPUP) + { + swtBotShell.bot().text().setText(text); + itemToSelect = swtBotShell.bot().widget(allOf(widgetOfType(Label.class), withText(text))); + } + else + { + itemToSelect = swtBotShell.bot().widget(allOf(widgetOfType(Label.class), withText(text))); + } + + Point itemToSelectLocation = syncExec((Result) itemToSelect::getLocation); + clickOnInternalWidget(itemToSelectLocation.x, itemToSelectLocation.y, itemToSelect); + return this; + } + + public void scrollToBottom(ScrolledComposite scrolledComposite) + { + syncExec(() -> { + scrolledComposite.setOrigin(0, scrolledComposite.getClientArea().height); + }); + } + + public boolean isTargetPresent(String text) + { + click(); + + try + { + SWTBotShell swtBotShell = bot().shellWithId(LaunchBarWidgetIds.POPUP); + ScrolledComposite scrolledComposite = swtBotShell.bot().widget(widgetOfType(ScrolledComposite.class)); + + int numberOfItemsInScrolledComp = syncExec(() -> + ((Composite) scrolledComposite.getChildren()[0]).getChildren().length + ); + + // Scroll to the bottom if there are many items + if (numberOfItemsInScrolledComp > NUM_FOR_FILTER_POPUP) + { + scrollToBottom(swtBotShell.bot().widget(widgetOfType(ScrolledComposite.class))); + swtBotShell.bot().text().setText(text); + + List labels = swtBotShell.bot().widgets(widgetOfType(Label.class)); + for (Label label : labels) + { + String labelText = syncExec(label::getText); + if (labelText.equals(text)) + { + return true; + } + } + return false; + } + else + { + Widget itemToCheck = swtBotShell.bot().widget(withText(text)); + String labelText = syncExec(() -> ((Label) itemToCheck).getText()); + return labelText.equals(text); + } + } + catch (WidgetNotFoundException e) + { + return false; + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } } }