Skip to content

Commit d834cd1

Browse files
authored
refactor backup of components yml and pioarduino-build.py
1 parent 68ff79d commit d834cd1

File tree

1 file changed

+140
-28
lines changed

1 file changed

+140
-28
lines changed

builder/frameworks/component_manager.py

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def __init__(self, env):
3030
# Simple logging attributes
3131
self.component_changes: List[str] = []
3232

33+
# MCU-specific backup tracking
34+
self.backup_created_per_mcu = {} # Track backups per MCU
35+
self.yaml_backup_created = False
36+
3337
self.arduino_framework_dir = self.platform.get_package_dir("framework-arduinoespressif32")
3438
self.arduino_libs_mcu = join(self.platform.get_package_dir("framework-arduinoespressif32-libs"), self.mcu)
3539

@@ -52,15 +56,16 @@ def handle_component_settings(self, add_components: bool = False, remove_compone
5256
remove_components: Whether to process component removals
5357
"""
5458

55-
# Create backup before first component removal and alwyas when a component is added
56-
if remove_components and not self.removed_components or add_components:
57-
self._backup_pioarduino_build_py()
58-
self._log_change("Created backup of build file")
59+
# Create MCU-specific backup before first component removal and on every add of a component
60+
if remove_components and not self.backup_created_per_mcu.get(self.mcu, False) or add_components:
61+
if self._backup_pioarduino_build_py():
62+
self._log_change(f"Created MCU backup for {self.mcu}")
5963

6064
# Check if env and GetProjectOption are available
61-
if hasattr(self, 'env') or hasattr(self.env, 'GetProjectOption'):
65+
if hasattr(self, 'env') and hasattr(self.env, 'GetProjectOption'):
6266
component_yml_path = self._get_or_create_component_yml()
6367
component_data = self._load_component_yml(component_yml_path)
68+
original_data = component_data.copy()
6469

6570
if remove_components:
6671
try:
@@ -80,7 +85,9 @@ def handle_component_settings(self, add_components: bool = False, remove_compone
8085
except Exception as e:
8186
self._log_change(f"Error adding components: {str(e)}")
8287

83-
self._save_component_yml(component_yml_path, component_data)
88+
# Only save if changes were made
89+
if component_data != original_data:
90+
self._save_component_yml(component_yml_path, component_data)
8491

8592
# Clean up removed components
8693
if self.removed_components:
@@ -94,10 +101,6 @@ def handle_component_settings(self, add_components: bool = False, remove_compone
94101

95102
def handle_lib_ignore(self) -> None:
96103
"""Handle lib_ignore entries from platformio.ini and remove corresponding includes."""
97-
# Create backup before processing lib_ignore
98-
if not self.ignored_libs:
99-
self._backup_pioarduino_build_py()
100-
101104
# Get lib_ignore entries from current environment only
102105
lib_ignore_entries = self._get_lib_ignore_entries()
103106

@@ -515,6 +518,10 @@ def _remove_ignored_lib_includes(self) -> None:
515518

516519
# Validate and write changes
517520
if self._validate_changes(original_content, content) and content != original_content:
521+
# Ensure MCU-specific backup exists before modification
522+
if not self.backup_created_per_mcu.get(self.mcu, False):
523+
self._backup_pioarduino_build_py()
524+
518525
with open(build_py_path, 'w') as f:
519526
f.write(content)
520527
self._log_change(f"Successfully updated build file with {total_removed} total removals")
@@ -550,12 +557,28 @@ def _get_or_create_component_yml(self) -> str:
550557
self._log_change(f"Created new component.yml file at {project_yml}")
551558
return project_yml
552559

553-
def _create_backup(self, file_path: str) -> None:
554-
"""Create backup of a file."""
560+
def _create_backup(self, file_path: str) -> bool:
561+
"""
562+
Create single backup of a file if it doesn't exist.
563+
564+
Args:
565+
file_path: Path to file to backup
566+
567+
Returns:
568+
True if backup was created or already exists
569+
"""
555570
backup_path = f"{file_path}.orig"
556-
if not os.path.exists(backup_path):
557-
shutil.copy(file_path, backup_path)
558-
self._log_change(f"Created backup: {backup_path}")
571+
572+
if os.path.exists(file_path) and not os.path.exists(backup_path):
573+
try:
574+
shutil.copy2(file_path, backup_path)
575+
self._log_change(f"Single backup created: {backup_path}")
576+
return True
577+
except Exception as e:
578+
self._log_change(f"Backup failed: {str(e)}")
579+
return False
580+
581+
return os.path.exists(backup_path)
559582

560583
def _create_default_component_yml(self, file_path: str) -> None:
561584
"""Create a default idf_component.yml file."""
@@ -577,13 +600,37 @@ def _load_component_yml(self, file_path: str) -> Dict[str, Any]:
577600
return {"dependencies": {}}
578601

579602
def _save_component_yml(self, file_path: str, data: Dict[str, Any]) -> None:
580-
"""Save component data to YAML file."""
603+
"""
604+
Save component data to YAML file only if changed.
605+
606+
Args:
607+
file_path: Path to YAML file
608+
data: Data to save
609+
"""
581610
try:
611+
# Check if content would actually change
612+
if os.path.exists(file_path):
613+
with open(file_path, "r") as f:
614+
existing_data = yaml.load(f, Loader=SafeLoader) or {}
615+
616+
# Compare data structures
617+
if existing_data == data:
618+
self._log_change(f"YAML unchanged, skipping write: {file_path}")
619+
return
620+
621+
# Create backup before first modification (only once)
622+
if not self.yaml_backup_created:
623+
self._create_backup(file_path)
624+
self.yaml_backup_created = True
625+
626+
# Write only if changed
582627
with open(file_path, "w") as f:
583-
yaml.dump(data, f)
584-
self._log_change(f"Saved component configuration to {file_path}")
628+
yaml.dump(data, f, default_flow_style=False, sort_keys=True)
629+
630+
self._log_change(f"YAML updated: {file_path}")
631+
585632
except Exception as e:
586-
self._log_change(f"Error saving component configuration: {str(e)}")
633+
self._log_change(f"Error saving YAML: {str(e)}")
587634

588635
def _remove_components(self, component_data: Dict[str, Any], components_to_remove: list) -> None:
589636
"""Remove specified components from the configuration."""
@@ -628,17 +675,34 @@ def _convert_component_name_to_filesystem(self, component_name: str) -> str:
628675
"""Convert component name from registry format to filesystem format."""
629676
return component_name.replace("/", "__")
630677

631-
def _backup_pioarduino_build_py(self) -> None:
632-
"""Create backup of the original pioarduino-build.py."""
678+
def _backup_pioarduino_build_py(self) -> bool:
679+
"""Create MCU-specific backup of pioarduino-build.py only once per MCU."""
633680
if "arduino" not in self.env.subst("$PIOFRAMEWORK"):
634-
return
681+
return False
682+
683+
# Check if backup already created for this MCU in this session
684+
if self.backup_created_per_mcu.get(self.mcu, False):
685+
return True
635686

636687
build_py_path = join(self.arduino_libs_mcu, "pioarduino-build.py")
637688
backup_path = join(self.arduino_libs_mcu, f"pioarduino-build.py.{self.mcu}")
638689

690+
# Only create backup if source exists and MCU-specific backup doesn't exist
639691
if os.path.exists(build_py_path) and not os.path.exists(backup_path):
640-
shutil.copy2(build_py_path, backup_path)
641-
self._log_change(f"Created backup of pioarduino-build.py for {self.mcu}")
692+
try:
693+
shutil.copy2(build_py_path, backup_path)
694+
self.backup_created_per_mcu[self.mcu] = True
695+
self._log_change(f"Created MCU-specific backup for {self.mcu}: {backup_path}")
696+
return True
697+
except Exception as e:
698+
self._log_change(f"MCU backup creation failed for {self.mcu}: {str(e)}")
699+
return False
700+
elif os.path.exists(backup_path):
701+
self.backup_created_per_mcu[self.mcu] = True
702+
self._log_change(f"MCU backup already exists for {self.mcu}")
703+
return True
704+
705+
return False
642706

643707
def _cleanup_removed_components(self) -> None:
644708
"""Clean up removed components and restore original build file."""
@@ -688,11 +752,59 @@ def _remove_cpppath_entries(self) -> None:
688752
self._log_change(f"Error cleaning up CPPPATH entries: {str(e)}")
689753

690754
def restore_pioarduino_build_py(self, source=None, target=None, env=None) -> None:
691-
"""Restore the original pioarduino-build.py from backup."""
755+
"""
756+
Restore the MCU-specific pioarduino-build.py from backup.
757+
758+
Args:
759+
source: Build source (unused)
760+
target: Build target (unused)
761+
env: Environment (unused)
762+
"""
692763
build_py_path = join(self.arduino_libs_mcu, "pioarduino-build.py")
693764
backup_path = join(self.arduino_libs_mcu, f"pioarduino-build.py.{self.mcu}")
694765

695766
if os.path.exists(backup_path):
696-
shutil.copy2(backup_path, build_py_path)
697-
os.remove(backup_path)
698-
self._log_change("Restored original pioarduino-build.py from backup")
767+
try:
768+
shutil.copy2(backup_path, build_py_path)
769+
self._log_change(f"Restored MCU-specific build file for {self.mcu}")
770+
771+
except Exception as e:
772+
self._log_change(f"Failed to restore MCU backup for {self.mcu}: {str(e)}")
773+
else:
774+
self._log_change(f"No MCU backup found for {self.mcu}")
775+
776+
def get_mcu_backup_status(self) -> Dict[str, bool]:
777+
"""
778+
Get status of MCU-specific backups.
779+
780+
Returns:
781+
Dictionary with MCU types as keys and backup existence as values
782+
"""
783+
backup_dir = self.arduino_libs_mcu
784+
mcu_types = ["esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c6", "esp32h2"]
785+
786+
status = {}
787+
for mcu in mcu_types:
788+
backup_path = join(backup_dir, f"pioarduino-build.py.{mcu}")
789+
status[mcu] = os.path.exists(backup_path)
790+
791+
return status
792+
793+
def get_changes_summary(self) -> List[str]:
794+
"""
795+
Get simple list of all changes made.
796+
797+
Returns:
798+
List of change messages
799+
"""
800+
return self.component_changes.copy()
801+
802+
def print_changes_summary(self) -> None:
803+
"""Print a simple summary of all changes."""
804+
if self.component_changes:
805+
print("\n=== Component Manager Changes ===")
806+
for change in self.component_changes:
807+
print(f" {change}")
808+
print("=" * 35)
809+
else:
810+
print("[ComponentManager] No changes made")

0 commit comments

Comments
 (0)