@@ -92,6 +92,7 @@ def __init__(self, settings: AppSettings) -> None:
9292 self ._pdk_candidates_available = False
9393 self ._pdk_preflight = None
9494 self ._pdk_source_preflight = None
95+ self ._pdk_bundle_preflight = None
9596 self ._runner_action = ""
9697
9798 self .validate_btn = QPushButton (pick (self .lang , "Validar entorno" , "Validate environment" ))
@@ -101,6 +102,7 @@ def __init__(self, settings: AppSettings) -> None:
101102 self .detect_pdk_btn = QPushButton (pick (self .lang , "Buscar PDK reutilizable" , "Find reusable PDK" ))
102103 self .use_pdk_btn = QPushButton (pick (self .lang , "Usar PDK seleccionado" , "Use selected PDK" ))
103104 self .install_managed_pdk_btn = QPushButton (pick (self .lang , "Instalar PDK gestionado" , "Install managed PDK" ))
105+ self .install_bundle_pdk_btn = QPushButton (pick (self .lang , "Descargar bundle PDK" , "Download PDK bundle" ))
104106 self .check_source_build_btn = QPushButton (pick (self .lang , "Precheck build desde fuentes" , "Source-build precheck" ))
105107 self .build_from_sources_btn = QPushButton (pick (self .lang , "Build PDK desde fuentes" , "Build PDK from sources" ))
106108 self .pdk_candidate_combo = QComboBox ()
@@ -109,6 +111,8 @@ def __init__(self, settings: AppSettings) -> None:
109111 self .pdk_candidate_summary .setWordWrap (True )
110112 self .pdk_preflight_summary = QLabel ()
111113 self .pdk_preflight_summary .setWordWrap (True )
114+ self .pdk_bundle_summary = QLabel ()
115+ self .pdk_bundle_summary .setWordWrap (True )
112116 self .pdk_source_preflight_summary = QLabel ()
113117 self .pdk_source_preflight_summary .setWordWrap (True )
114118 self .prev_btn = QPushButton (pick (self .lang , "Atrás" , "Back" ))
@@ -357,10 +361,13 @@ def _build_pdk_page(self) -> QWidget:
357361 actions .addWidget (self .detect_pdk_btn )
358362 actions .addWidget (self .use_pdk_btn )
359363 actions .addWidget (self .install_managed_pdk_btn )
364+ actions .addWidget (self .install_bundle_pdk_btn )
360365 actions .addStretch (1 )
361366 layout .addLayout (actions )
362367 layout .addWidget (QLabel (pick (self .lang , "Preflight de instalación gestionada" , "Managed install preflight" )))
363368 layout .addWidget (self .pdk_preflight_summary )
369+ layout .addWidget (QLabel (pick (self .lang , "Bundle PDK" , "PDK bundle" )))
370+ layout .addWidget (self .pdk_bundle_summary )
364371 source_actions = QHBoxLayout ()
365372 source_actions .addWidget (self .check_source_build_btn )
366373 source_actions .addWidget (self .build_from_sources_btn )
@@ -451,6 +458,7 @@ def _wire(self) -> None:
451458 self .detect_pdk_btn .clicked .connect (self .refresh_pdk_candidates )
452459 self .use_pdk_btn .clicked .connect (self .apply_selected_pdk_candidate )
453460 self .install_managed_pdk_btn .clicked .connect (self .install_managed_pdk )
461+ self .install_bundle_pdk_btn .clicked .connect (self .install_bundle_pdk )
454462 self .check_source_build_btn .clicked .connect (self .refresh_pdk_source_preflight )
455463 self .build_from_sources_btn .clicked .connect (self .build_pdk_from_sources )
456464 self .pdk_candidate_combo .currentIndexChanged .connect (self ._update_pdk_candidate_summary )
@@ -581,6 +589,7 @@ def refresh_pdk_candidates(self) -> None:
581589 if self .pdk_candidate_combo .count () and self .pdk_candidate_combo .currentIndex () < 0 :
582590 self .pdk_candidate_combo .setCurrentIndex (0 )
583591 self ._refresh_pdk_preflight ()
592+ self .refresh_pdk_bundle_preflight ()
584593 self .refresh_pdk_source_preflight ()
585594 self ._update_pdk_candidate_summary ()
586595 self ._sync_action_gates ()
@@ -661,6 +670,64 @@ def refresh_pdk_source_preflight(self) -> None:
661670 )
662671 self ._sync_action_gates ()
663672
673+ def refresh_pdk_bundle_preflight (self ) -> None :
674+ self ._pdk_bundle_preflight = self .setup_mgr .pdk_bundle_preflight (self .settings )
675+ summary = self ._pdk_bundle_preflight
676+ self .pdk_bundle_summary .setText (
677+ pick (
678+ self .lang ,
679+ f"Bundle: { summary .bundle_name } \n "
680+ f"Versión: { summary .bundle_version or 'pendiente' } \n "
681+ f"Instalación TT: { summary .target_sky130a } \n "
682+ f"Cache: { summary .cache_root } \n "
683+ f"Espacio libre: { summary .free_bytes / (1024 ** 3 ):.1f} GB\n "
684+ f"Asset configurado: { 'sí' if bool (summary .asset_url and summary .asset_filename ) else 'no' } \n "
685+ f"Listo para instalar: { 'sí' if summary .ready else 'no' } " ,
686+ f"Bundle: { summary .bundle_name } \n "
687+ f"Version: { summary .bundle_version or 'pending' } \n "
688+ f"TT install: { summary .target_sky130a } \n "
689+ f"Cache: { summary .cache_root } \n "
690+ f"Free space: { summary .free_bytes / (1024 ** 3 ):.1f} GB\n "
691+ f"Asset configured: { 'yes' if bool (summary .asset_url and summary .asset_filename ) else 'no' } \n "
692+ f"Ready to install: { 'yes' if summary .ready else 'no' } " ,
693+ )
694+ )
695+ self ._sync_action_gates ()
696+
697+ def install_bundle_pdk (self ) -> None :
698+ self .refresh_pdk_bundle_preflight ()
699+ summary = self ._pdk_bundle_preflight
700+ if summary is None or not summary .ready :
701+ self .log .append (
702+ pick (
703+ self .lang ,
704+ "El bundle PDK todavía no está listo para descargarse e instalarse. Revisa el panel del bundle.\n " ,
705+ "The PDK bundle is not ready to download and install yet. Review the bundle panel.\n " ,
706+ )
707+ )
708+ return
709+ script_path = self .setup_mgr .pdk_bundle_install_script ()
710+ if not script_path .exists ():
711+ self .log .append (
712+ pick (
713+ self .lang ,
714+ f"Script del bundle no encontrado: { script_path } \n " ,
715+ f"Bundle script not found: { script_path } \n " ,
716+ )
717+ )
718+ return
719+ self ._begin_activity (pick (self .lang , "Descargando bundle PDK..." , "Downloading PDK bundle..." ))
720+ self .log .append (
721+ pick (
722+ self .lang ,
723+ "Descargando e instalando el bundle PDK en la ruta canónica del usuario. Esto puede tardar bastante.\n " ,
724+ "Downloading and installing the PDK bundle into the user's canonical path. This can take a while.\n " ,
725+ )
726+ )
727+ self .send_status .emit (pick (self .lang , "Instalando bundle PDK" , "Installing PDK bundle" ))
728+ self ._runner_action = "install_pdk_bundle"
729+ self .runner .run (CommandSpec (command = self .setup_mgr .pdk_bundle_install_command ()))
730+
664731 def build_pdk_from_sources (self ) -> None :
665732 self .refresh_pdk_source_preflight ()
666733 summary = self ._pdk_source_preflight
@@ -756,6 +823,22 @@ def _on_finished(self, code: int, status: str) -> None:
756823 self ._finish_activity (True , pick (self .lang , "Build de PDK listo" , "PDK build ready" ))
757824 return
758825
826+ if code == 0 and action == "install_pdk_bundle" :
827+ self .log .append (
828+ pick (
829+ self .lang ,
830+ "\n Bundle PDK instalado. Refrescando detección y validación.\n " ,
831+ "\n PDK bundle installed. Refreshing detection and validation.\n " ,
832+ )
833+ )
834+ self .send_status .emit (pick (self .lang , "Bundle PDK listo" , "PDK bundle ready" ))
835+ self .refresh_detection ()
836+ self .refresh_validation ()
837+ self ._apply_detected_defaults (automatic = True )
838+ self ._set_step (3 )
839+ self ._finish_activity (True , pick (self .lang , "Bundle PDK listo" , "PDK bundle ready" ))
840+ return
841+
759842 if action == "build_pdk_sources" :
760843 self .log .append (
761844 pick (
@@ -769,6 +852,19 @@ def _on_finished(self, code: int, status: str) -> None:
769852 self ._finish_activity (False , pick (self .lang , "Build de PDK falló" , "PDK build failed" ))
770853 return
771854
855+ if action == "install_pdk_bundle" :
856+ self .log .append (
857+ pick (
858+ self .lang ,
859+ f"\n La instalación del bundle PDK falló (exit={ code } , status={ status } ). Revisa el log y la configuración del asset.\n " ,
860+ f"\n PDK bundle installation failed (exit={ code } , status={ status } ). Review the log and the asset configuration.\n " ,
861+ )
862+ )
863+ self .send_status .emit (pick (self .lang , "Bundle PDK falló" , "PDK bundle failed" ))
864+ self .refresh_pdk_bundle_preflight ()
865+ self ._finish_activity (False , pick (self .lang , "Bundle PDK falló" , "PDK bundle failed" ))
866+ return
867+
772868 if action == "install_tools" :
773869 self .log .append (
774870 pick (
@@ -938,6 +1034,7 @@ def _set_action_buttons_enabled(self, enabled: bool) -> None:
9381034 self .detect_pdk_btn .setEnabled (False )
9391035 self .use_pdk_btn .setEnabled (False )
9401036 self .install_managed_pdk_btn .setEnabled (False )
1037+ self .install_bundle_pdk_btn .setEnabled (False )
9411038 self .check_source_build_btn .setEnabled (False )
9421039 self .build_from_sources_btn .setEnabled (False )
9431040 self .prev_btn .setEnabled (False )
@@ -964,6 +1061,11 @@ def _sync_action_gates(self) -> None:
9641061 and self ._pdk_preflight .existing_status == "missing"
9651062 and bool (self ._pdk_preflight .selected_candidate )
9661063 )
1064+ self .install_bundle_pdk_btn .setEnabled (
1065+ self ._verification_completed
1066+ and self ._pdk_bundle_preflight is not None
1067+ and self ._pdk_bundle_preflight .ready
1068+ )
9671069 self .check_source_build_btn .setEnabled (self ._verification_completed )
9681070 self .build_from_sources_btn .setEnabled (
9691071 self ._verification_completed
@@ -1022,6 +1124,21 @@ def _sync_action_gates(self) -> None:
10221124 "Install a managed PDK in the configured canonical path." ,
10231125 )
10241126 )
1127+ self .install_bundle_pdk_btn .setToolTip (
1128+ pick (
1129+ self .lang ,
1130+ "Primero verifica el sistema y configura un asset del bundle PDK en el manifiesto." if not self ._verification_completed else
1131+ "El bundle PDK sigue bloqueado por configuración, espacio o porque ya existe el destino." if (
1132+ self ._pdk_bundle_preflight is None or not self ._pdk_bundle_preflight .ready
1133+ ) else
1134+ "Descarga e instala un bundle `sky130A` ya preparado en `~/pdk`." ,
1135+ "Verify the system first and configure a PDK bundle asset in the manifest." if not self ._verification_completed else
1136+ "The PDK bundle is still blocked by configuration, space, or because the target already exists." if (
1137+ self ._pdk_bundle_preflight is None or not self ._pdk_bundle_preflight .ready
1138+ ) else
1139+ "Download and install a prebuilt `sky130A` bundle into `~/pdk`." ,
1140+ )
1141+ )
10251142 self .build_from_sources_btn .setToolTip (
10261143 pick (
10271144 self .lang ,
0 commit comments