Skip to content

Refactor Arduino build part #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open

Refactor Arduino build part #186

wants to merge 21 commits into from

Conversation

Jason2866
Copy link

@Jason2866 Jason2866 commented May 24, 2025

Description:

Related issue (if applicable): fixes #

Checklist:

  • The pull request is done against the latest develop branch
  • Only relevant files were touched
  • Only one feature/fix was added per PR, more changes are allowed when changing boards.json
  • I accept the CLA

Summary by CodeRabbit

  • Refactor

    • Improved internal structure, readability, and maintainability of the Arduino framework build script and Espressif32 platform setup.
    • Enhanced modularity, caching, and error handling across build and installation processes.
    • Added robust file operation handling and logging for better reliability.
  • Bug Fixes

    • Improved handling of configuration files, sdkconfig matching, and environment setup.
    • Enhanced version checks, reinstallation logic, and retry mechanisms for tool and package installations.
  • Performance

    • Optimized repeated operations with caching for faster environment and package checks.

Copy link

coderabbitai bot commented May 24, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The arduino.py module was extensively refactored for improved structure, type safety, and maintainability, adding modular functions, caching, error handling, and better path handling. The platform.py module's Espressif32Platform class was also refactored with modular methods for tool installation, version checks, framework and MCU configuration, filesystem and board-specific tools, and debug session setup.

Changes

File(s) Change Summary
builder/frameworks/arduino.py Refactored for structure and clarity; added constants, type hints, caching, improved error handling, and modular functions. Introduced new functions for ESP32-Solo1 config, pip package management, sdkconfig matching, and framework reinstallation. Switched to pathlib for path handling.
platform.py Refactored Espressif32Platform class with modular methods for tool installation, version checking, framework and MCU toolchain configuration, filesystem and board-specific tools, and debug session setup. Added error handling, caching, constants, and logging. Introduced helper decorators for safe file operations and a new ToolInstallationError exception.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ArduinoModule
    participant Pip
    participant FileSystem

    User->>ArduinoModule: Run build/setup
    ArduinoModule->>ArduinoModule: setup_esp32_solo1_config()
    ArduinoModule->>ArduinoModule: install_python_deps()
    ArduinoModule->>Pip: Query installed packages (cached)
    ArduinoModule->>Pip: Install missing dependencies if needed
    ArduinoModule->>ArduinoModule: check_reinstall_framework()
    alt Framework needs reinstall
        ArduinoModule->>FileSystem: Clean sdkconfig and framework directories
        ArduinoModule->>Pip: Reinstall packages from specs
    end
    ArduinoModule->>FileSystem: Detect and select build scripts
    ArduinoModule-->>User: Build/setup complete
Loading
sequenceDiagram
    participant User
    participant EspressifPlatform
    participant ToolManager
    participant RemoteServer
    participant FileSystem

    User->>EspressifPlatform: configure_default_packages(variables, targets)
    EspressifPlatform->>ToolManager: check_tool_status(tool_name)
    alt Tool not installed or outdated
        EspressifPlatform->>RemoteServer: fetch_tool_package(tool_name)
        RemoteServer-->>EspressifPlatform: package_data
        EspressifPlatform->>ToolManager: install_tool(tool_name)
        ToolManager->>FileSystem: install_files(tool_name)
    else Tool installed and valid
        EspressifPlatform->>ToolManager: skip_installation(tool_name)
    end
    EspressifPlatform->>EspressifPlatform: configure_frameworks(frameworks)
    EspressifPlatform->>EspressifPlatform: configure_mcu_toolchains(mcu, variables, targets)
    EspressifPlatform->>EspressifPlatform: configure_filesystem_tools(variables, targets)
    EspressifPlatform->>EspressifPlatform: configure_board_tools(variables)
    EspressifPlatform-->>User: configuration_complete
Loading

Possibly related PRs

  • tl installer / new OpenOCD  #149: Refactors and enhances Arduino framework build script and Espressif32Platform class with modularity, caching, and error handling, closely related to tool installation and framework setup improvements in this PR.

Poem

🐇 In code’s green meadow where the bunnies play,
Paths now clear with pathlib’s sway.
Cached and checked, dependencies aligned,
Tools installed with care refined.
Frameworks dance in modular grace,
This rabbit’s hop sets a steady pace! 🌿✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Jason2866
Copy link
Author

@coderabbitai full review

Copy link

coderabbitai bot commented May 24, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d84d32 and 977a629.

📒 Files selected for processing (1)
  • builder/frameworks/arduino.py (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
builder/frameworks/arduino.py (1)
builder/frameworks/espidf.py (1)
  • install_python_deps (1507-1577)
🪛 Ruff (0.11.9)
builder/frameworks/arduino.py

34-34: typing.Optional imported but unused

Remove unused import: typing.Optional

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (18)
  • GitHub Check: build (windows-latest, examples/espidf-ulp-lp)
  • GitHub Check: build (macos-15, examples/tasmota)
  • GitHub Check: build (macos-15, examples/espidf-arduino-matter-light)
  • GitHub Check: build (macos-15, examples/arduino-usb-keyboard)
  • GitHub Check: build (macos-15, examples/arduino-blink)
  • GitHub Check: build (windows-latest, examples/espidf-hello-world)
  • GitHub Check: build (windows-latest, examples/espidf-arduino-littlefs)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-ulp-riscv)
  • GitHub Check: build (windows-latest, examples/arduino-NimBLE-ext_client)
  • GitHub Check: build (windows-latest, examples/tasmota)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-peripherals-usb)
  • GitHub Check: build (windows-latest, examples/arduino-rmt-blink)
  • GitHub Check: build (ubuntu-24.04, examples/tasmota)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-arduino-blink)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-arduino-matter-light)
  • GitHub Check: build (ubuntu-24.04, examples/arduino-NimBLE-ext_client)
  • GitHub Check: build (ubuntu-24.04, examples/arduino-wifiscan)
  • GitHub Check: build (ubuntu-24.04, examples/arduino-blink)
🔇 Additional comments (6)
builder/frameworks/arduino.py (6)

89-104: Well-structured ESP32-Solo1 configuration function.

The function effectively modularizes Solo1-specific setup with clear conditions and proper build flag handling.


105-137: Excellent implementation of Python dependency management with caching.

The refactored dependency installation logic brings significant improvements:

  • Performance optimization through @lru_cache
  • Comprehensive error handling including timeout protection
  • Clean version checking using semantic versioning
  • More concise pip install command construction

This implementation is superior to the similar function in espidf.py and could serve as a template for future refactoring.


138-181: Well-designed sdkconfig management with smart caching.

The sdkconfig management functions demonstrate excellent design:

  • Appropriate use of MD5 for cache invalidation (not security)
  • Smart caching strategy on matching_custom_sdkconfig
  • Clean separation of concerns between checking and decision-making
  • Robust error handling with pathlib

206-222: Robust cross-platform path handling.

The is_framework_subfolder function shows excellent attention to Windows compatibility with drive letter handling and proper exception handling for path comparison edge cases.


245-278: Comprehensive framework reinstallation logic.

The reinstall_framework function provides a clean, systematic approach:

  • Proper cleanup of sdkconfig files across all environments
  • Safe directory removal with existence checks
  • Smart URL extraction from package specs
  • Consistent use of pathlib for path operations

This is a significant improvement in maintainability and reliability.


279-318: Clean and logical main execution flow.

The refactored main execution flow demonstrates excellent organization:

  • Clear setup sequence with proper initialization order
  • Smart conditional handling of framework configurations
  • Improved build script detection using pathlib
  • Maintained backward compatibility while enhancing functionality

The overall refactoring significantly improves code maintainability and robustness.

@Jason2866
Copy link
Author

@coderabbitai full review

Copy link

coderabbitai bot commented May 25, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
builder/frameworks/arduino.py (1)

34-34: Remove unused import Optional.

The static analysis correctly identified that Optional is imported but never used in the code.

Apply this diff to fix the import:

-from typing import Dict, List, Tuple, Optional
+from typing import Dict, List, Tuple
🧹 Nitpick comments (1)
platform.py (1)

320-320: Rename unused loop variable.

The loop control variable arch_name is not used within the loop body.

Apply this diff to follow Python conventions:

-        for arch_name, config in MCU_TOOLCHAIN_CONFIG.items():
+        for _, config in MCU_TOOLCHAIN_CONFIG.items():
🧰 Tools
🪛 Ruff (0.11.9)

320-320: Loop control variable arch_name not used within loop body

Rename unused arch_name to _arch_name

(B007)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d84d32 and 56da2c0.

📒 Files selected for processing (2)
  • builder/frameworks/arduino.py (2 hunks)
  • platform.py (5 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
builder/frameworks/arduino.py (1)
builder/frameworks/espidf.py (1)
  • install_python_deps (1507-1577)
🪛 Ruff (0.11.9)
platform.py

25-25: os.path.join imported but unused

Remove unused import: os.path.join

(F401)


170-170: Local variable devnull is assigned to but never used

Remove assignment to unused variable devnull

(F841)


276-276: Use of functools.lru_cache or functools.cache on methods can lead to memory leaks

(B019)


320-320: Loop control variable arch_name not used within loop body

Rename unused arch_name to _arch_name

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: build (macos-15, examples/espidf-http-request)
  • GitHub Check: build (macos-15, examples/espidf-hello-world)
  • GitHub Check: build (windows-latest, examples/espidf-ulp-riscv)
  • GitHub Check: build (macos-15, examples/arduino-blink)
  • GitHub Check: build (windows-latest, examples/espidf-ulp-lp)
  • GitHub Check: build (windows-latest, examples/espidf-ulp)
  • GitHub Check: build (windows-latest, examples/espidf-blink)
  • GitHub Check: build (windows-latest, examples/espidf-http-request)
  • GitHub Check: build (windows-latest, examples/arduino-matter-light)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-ulp)
  • GitHub Check: build (windows-latest, examples/arduino-wifiscan)
  • GitHub Check: build (windows-latest, examples/espidf-arduino-blink)
  • GitHub Check: build (windows-latest, examples/arduino-blink)
  • GitHub Check: build (windows-latest, examples/tasmota)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-coap-server)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-blink)
  • GitHub Check: build (windows-latest, examples/arduino-NimBLE-ext_client)
  • GitHub Check: build (ubuntu-24.04, examples/arduino-NimBLE-ext_client)
  • GitHub Check: build (ubuntu-24.04, examples/espidf-arduino-matter-light)
  • GitHub Check: build (ubuntu-24.04, examples/arduino-blink)
🔇 Additional comments (5)
builder/frameworks/arduino.py (3)

104-117: Well-implemented caching for pip packages.

The use of @lru_cache and comprehensive error handling (timeout, subprocess errors, JSON decode errors) makes this function robust and efficient.


214-230: Excellent Windows path handling improvements.

The addition of drive letter checks and proper exception handling for os.path.commonpath makes this function robust across different platforms.


253-286: Clean implementation of framework reinstallation.

The use of pathlib for file operations and comprehensive cleanup of sdkconfig files across all environments is well done.

platform.py (2)

89-119: Excellent error handling pattern for file operations.

The safe_file_operation decorator and the safe file operation functions provide robust error handling with proper logging, making the code more maintainable and debuggable.


459-490: Well-structured configuration method with proper error handling.

The modularized approach with individual configuration methods and the top-level exception handling that logs errors without breaking compatibility is a good design choice.

@Jason2866
Copy link
Author

@coderabbitai full review

Copy link

coderabbitai bot commented May 25, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
builder/frameworks/arduino.py (1)

34-34: Remove unused import Optional.

The static analysis correctly identified that Optional is imported but never used in the code.

Apply this diff to fix the import:

-from typing import Dict, List, Tuple, Optional
+from typing import Dict, List, Tuple
platform.py (2)

25-25: Remove unused import join.

The static analysis correctly identified that join is imported but never used in the code. All path operations use os.path.join directly or pathlib.

Apply this diff to fix the import:

-from os.path import join
🧰 Tools
🪛 Ruff (0.11.9)

25-25: os.path.join imported but unused

Remove unused import: os.path.join

(F401)


280-290: Potential memory leak with @lru_cache on instance method.

Using @lru_cache on instance methods can cause memory leaks as the cache holds references to self. Consider using instance-level caching.

Here's a safer alternative:

-    @lru_cache(maxsize=1)
     def _get_arduino_package_data(self) -> Optional[Dict]:
         """Cached HTTP request for Arduino package data"""
+        if hasattr(self, '_arduino_package_cache'):
+            return self._arduino_package_cache
+            
         try:
             response = requests.get(ARDUINO_ESP32_PACKAGE_URL, timeout=30)
             response.raise_for_status()
-            return response.json()
+            self._arduino_package_cache = response.json()
+            return self._arduino_package_cache
         except requests.RequestException as e:
             logger.error(f"Error fetching Arduino package data: {e}")
+            self._arduino_package_cache = None
             return None
🧰 Tools
🪛 Ruff (0.11.9)

280-280: Use of functools.lru_cache or functools.cache on methods can lead to memory leaks

(B019)

🧹 Nitpick comments (1)
platform.py (1)

493-496: Consider adding a debug flag for error visibility.

While maintaining compatibility is important, silently catching all exceptions could hide critical issues during development. Consider adding a debug mode:

         except Exception as e:
             logger.error(f"Error in package configuration: {type(e).__name__}: {e}")
+            if os.environ.get('PLATFORMIO_DEBUG'):
+                import traceback
+                traceback.print_exc()
             # Don't re-raise to maintain compatibility
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d84d32 and 849e12b.

📒 Files selected for processing (2)
  • builder/frameworks/arduino.py (2 hunks)
  • platform.py (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
builder/frameworks/arduino.py (1)
builder/frameworks/espidf.py (1)
  • install_python_deps (1507-1577)
🪛 Ruff (0.11.9)
platform.py

25-25: os.path.join imported but unused

Remove unused import: os.path.join

(F401)


280-280: Use of functools.lru_cache or functools.cache on methods can lead to memory leaks

(B019)

🔇 Additional comments (6)
builder/frameworks/arduino.py (3)

42-49: Good practice: Dependencies defined as constants.

Excellent refactoring to define Python dependencies as a typed constant, making them easy to maintain and update.


104-117: Well-implemented caching for pip package retrieval.

Good use of @lru_cache on a module-level function (avoids memory leak issues), comprehensive error handling, and sensible timeout value.


141-169: Robust SDK configuration matching with appropriate caching.

Good implementation with Path usage, proper error handling, and reasonable cache size for configuration variations.

platform.py (3)

93-104: Excellent error handling pattern with the decorator.

The safe_file_operation decorator provides a clean, reusable way to handle filesystem errors with appropriate logging.


175-180: Good fix: Removed unnecessary devnull handling.

The implementation now correctly uses subprocess.DEVNULL directly without the unnecessary with open(os.devnull) context manager.


322-337: Excellent caching pattern for instance methods.

This method demonstrates the correct way to implement caching for instance methods using instance attributes instead of @lru_cache. This pattern should be used for the _get_arduino_package_data method as well.

@Jason2866
Copy link
Author

@coderabbitai full review

Copy link

coderabbitai bot commented May 26, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
builder/frameworks/arduino.py (1)

283-293: URL extraction error handling properly implemented.

The error handling for URL extraction has been correctly implemented as suggested in the previous review.

platform.py (1)

279-293: Instance-level caching properly implemented.

The memory leak issue from using @lru_cache on instance methods has been correctly addressed by implementing instance-level caching.

🧹 Nitpick comments (3)
builder/frameworks/arduino.py (2)

97-98: Clarify the logic for checking build_unflags length.

The condition len(str(env.GetProjectOption("build_unflags"))) == 2 appears to check for an empty list representation "[]". Consider making this more explicit for better readability.

-        if len(str(env.GetProjectOption("build_unflags"))) == 2:
-            env['BUILD_UNFLAGS'] = {}
+        # Check if build_unflags is empty or not set
+        build_unflags_str = str(env.GetProjectOption("build_unflags"))
+        if build_unflags_str in ("[]", "{}") or not build_unflags_str:
+            env['BUILD_UNFLAGS'] = []

226-227: Simplify the commonpath comparison.

The call to os.path.commonpath([FRAMEWORK_SDK_DIR]) is redundant as it just returns FRAMEWORK_SDK_DIR.

-        return (os.path.commonpath([FRAMEWORK_SDK_DIR]) == 
-                os.path.commonpath([FRAMEWORK_SDK_DIR, potential_subfolder]))
+        return (FRAMEWORK_SDK_DIR == 
+                os.path.commonpath([FRAMEWORK_SDK_DIR, potential_subfolder]))
platform.py (1)

496-499: Consider re-raising critical exceptions.

Catching all exceptions without re-raising could hide critical errors. Consider logging and re-raising exceptions that indicate serious issues.

         except Exception as e:
             logger.error(f"Error in package configuration: {type(e).__name__}: {e}")
-            # Don't re-raise to maintain compatibility
+            # Re-raise critical exceptions, suppress minor ones for compatibility
+            if isinstance(e, (MemoryError, SystemError, KeyboardInterrupt)):
+                raise
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d84d32 and 1aaece9.

📒 Files selected for processing (2)
  • builder/frameworks/arduino.py (2 hunks)
  • platform.py (6 hunks)
🧰 Additional context used
🪛 Ruff (0.11.9)
platform.py

23-23: functools.lru_cache imported but unused

Remove unused import: functools.lru_cache

(F401)

🔇 Additional comments (4)
builder/frameworks/arduino.py (2)

104-117: Well-implemented caching for pip packages.

Good use of @lru_cache decorator and comprehensive error handling for subprocess operations.


141-169: Robust implementation of sdkconfig matching.

Good implementation with proper caching, error handling, and clear logic flow.

platform.py (2)

92-104: Well-designed error handling decorator.

Good implementation of the decorator pattern for safe filesystem operations with appropriate error handling and logging.


599-657: Well-structured debug configuration implementation.

The debug configuration methods are properly modularized with clear separation of concerns and comprehensive interface support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant