|
1 | 1 | # SPDX-License-Identifier: GPL-3.0-only |
2 | | -# Nuitka engine package - auto-import all submodules |
3 | | - |
4 | | -# Copyright (C) 2025 Samuel Amen Ague |
5 | | -# Author: Samuel Amen Ague |
| 2 | +# Nuitka engine package bootstrap |
6 | 3 | from __future__ import annotations |
7 | 4 |
|
8 | 5 | import importlib |
9 | 6 | import pkgutil |
10 | 7 |
|
11 | 8 | __all__: list[str] = [] |
12 | 9 |
|
13 | | -# Explicit registration to ensure the engine is available even if auto-import misses it |
14 | 10 | try: |
15 | 11 | from engine_sdk import registry as _registry # type: ignore |
16 | | - |
17 | | - from .engine import NuitkaEngine as _NuitkaEngine |
| 12 | + from .engine import NuitkaEngine |
18 | 13 |
|
19 | 14 | if _registry: |
20 | | - _registry.register(_NuitkaEngine) |
| 15 | + _registry.register(NuitkaEngine) |
| 16 | + __all__.append("NuitkaEngine") |
21 | 17 | except Exception: |
22 | 18 | pass |
23 | 19 |
|
24 | 20 |
|
25 | 21 | def _auto_import_all() -> None: |
26 | | - """ |
27 | | - Import all Python modules and subpackages contained in this package. |
28 | | - This ensures any engine definitions or side-effects are loaded on import. |
29 | | - """ |
| 22 | + """Import all nested modules to keep plugin side-effects available.""" |
30 | 23 | try: |
31 | 24 | pkg_name = __name__ |
32 | | - # __path__ is defined for packages; walk through all nested modules/packages |
33 | 25 | for _finder, name, _ispkg in pkgutil.walk_packages(__path__, prefix=f"{pkg_name}."): |
34 | 26 | try: |
35 | 27 | importlib.import_module(name) |
36 | | - # Optionally track short module names in __all__ |
37 | 28 | short = name.rsplit(".", 1)[-1] |
38 | 29 | if short not in __all__: |
39 | 30 | __all__.append(short) |
40 | 31 | except Exception: |
41 | | - # Avoid breaking package import if some submodule import fails |
42 | 32 | pass |
43 | 33 | except Exception: |
44 | | - # Be resilient to environments where __path__ is unusual |
45 | 34 | pass |
46 | 35 |
|
47 | 36 |
|
48 | | - return cmd |
49 | | - |
50 | | - except Exception as e: |
51 | | - try: |
52 | | - if hasattr(gui, "log"): |
53 | | - log_with_level( |
54 | | - gui, "error", f"Erreur construction commande Nuitka: {e}" |
55 | | - ) |
56 | | - except Exception: |
57 | | - pass |
58 | | - return [] |
59 | | - |
60 | | - def program_and_args(self, gui, file: str) -> Optional[tuple[str, list[str]]]: |
61 | | - """Return the program and args for QProcess.""" |
62 | | - cmd = self.build_command(gui, file) |
63 | | - if not cmd: |
64 | | - return None |
65 | | - return cmd[0], cmd[1:] |
66 | | - |
67 | | - def environment(self) -> Optional[dict[str, str]]: |
68 | | - """Return environment variables for the compilation process.""" |
69 | | - try: |
70 | | - env = {} |
71 | | - |
72 | | - # Set PYTHONIOENCODING for proper output handling |
73 | | - env["PYTHONIOENCODING"] = "utf-8" |
74 | | - |
75 | | - # Disable PYTHONUTF8 mode to avoid conflicts |
76 | | - env["PYTHONUTF8"] = "0" |
77 | | - |
78 | | - # Set LC_ALL for consistent output |
79 | | - env["LC_ALL"] = "C" |
80 | | - |
81 | | - return env if env else None |
82 | | - except Exception: |
83 | | - return None |
84 | | - |
85 | | - def on_success(self, gui, file: str) -> None: |
86 | | - """Handle successful compilation.""" |
87 | | - try: |
88 | | - # Log success message with output location |
89 | | - if ( |
90 | | - hasattr(self, "_nuitka_output_dir") |
91 | | - and self._nuitka_output_dir.text().strip() |
92 | | - ): |
93 | | - try: |
94 | | - if hasattr(gui, "log"): |
95 | | - log_with_level( |
96 | | - gui, |
97 | | - "success", |
98 | | - f"Compilation Nuitka terminée. Sortie dans: {self._nuitka_output_dir.text().strip()}", |
99 | | - ) |
100 | | - except Exception: |
101 | | - pass |
102 | | - except Exception: |
103 | | - pass |
104 | | - |
105 | | - def create_tab(self, gui): |
106 | | - """ |
107 | | - Create the Nuitka tab widget with all options. |
108 | | - Returns (widget, label) tuple or None if tab creation fails. |
109 | | - """ |
110 | | - try: |
111 | | - from PySide6.QtWidgets import ( |
112 | | - QCheckBox, |
113 | | - QFormLayout, |
114 | | - QLabel, |
115 | | - QGroupBox, |
116 | | - QSizePolicy, |
117 | | - QVBoxLayout, |
118 | | - QWidget, |
119 | | - ) |
120 | | - |
121 | | - # Create the tab widget |
122 | | - tab = QWidget() |
123 | | - tab.setObjectName("tab_nuitka_dynamic") |
124 | | - |
125 | | - # Create main layout |
126 | | - layout = QVBoxLayout(tab) |
127 | | - layout.setSpacing(8) |
128 | | - layout.setContentsMargins(8, 8, 8, 8) |
129 | | - |
130 | | - build_group = QGroupBox("Build", tab) |
131 | | - build_layout = QFormLayout() |
132 | | - build_layout.setSpacing(6) |
133 | | - |
134 | | - # Onefile option |
135 | | - self._nuitka_onefile = QCheckBox("Onefile (--onefile)") |
136 | | - self._nuitka_onefile.setObjectName("nuitka_onefile_dynamic") |
137 | | - build_layout.addRow("Mode:", self._nuitka_onefile) |
138 | | - |
139 | | - # Standalone option |
140 | | - self._nuitka_standalone = QCheckBox("Standalone (--standalone)") |
141 | | - self._nuitka_standalone.setObjectName("nuitka_standalone_dynamic") |
142 | | - build_layout.addRow("Type:", self._nuitka_standalone) |
143 | | - |
144 | | - # Disable console option |
145 | | - self._nuitka_disable_console = QCheckBox("Disable console") |
146 | | - self._nuitka_disable_console.setObjectName("nuitka_disable_console_dynamic") |
147 | | - self._nuitka_disable_console.setToolTip( |
148 | | - "Disable console window for Windows builds." |
149 | | - ) |
150 | | - build_layout.addRow("Console:", self._nuitka_disable_console) |
151 | | - build_group.setLayout(build_layout) |
152 | | - |
153 | | - output_group = QGroupBox("Output", tab) |
154 | | - output_layout = QVBoxLayout() |
155 | | - output_layout.setSpacing(6) |
156 | | - |
157 | | - # Output directory |
158 | | - self._nuitka_output_dir = add_output_dir( |
159 | | - output_layout, |
160 | | - "Dossier de sortie (--output-dir)", |
161 | | - "nuitka_output_dir_dynamic", |
162 | | - ) |
163 | | - output_group.setLayout(output_layout) |
164 | | - |
165 | | - assets_group = QGroupBox("Assets", tab) |
166 | | - assets_layout = QVBoxLayout() |
167 | | - assets_layout.setSpacing(6) |
168 | | - |
169 | | - # Icon button + path input |
170 | | - self._btn_nuitka_icon, self._nuitka_icon_path_input = add_icon_selector( |
171 | | - assets_layout, |
172 | | - "🎨 Choisir une icône (.ico) Nuitka", |
173 | | - self.select_icon, |
174 | | - "btn_nuitka_icon_dynamic", |
175 | | - "nuitka_icon_path_input_dynamic", |
176 | | - ) |
177 | | - if self._nuitka_icon_path_input is not None: |
178 | | - self._nuitka_icon_path_input.textChanged.connect( |
179 | | - self._on_icon_path_changed |
180 | | - ) |
181 | | - assets_group.setLayout(assets_layout) |
182 | | - |
183 | | - hint = QLabel( |
184 | | - "Tip: combine standalone or onefile modes carefully, then tune console visibility for desktop apps.", |
185 | | - tab, |
186 | | - ) |
187 | | - hint.setStyleSheet("color: #888; font-size: 11px;") |
188 | | - hint.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) |
189 | | - |
190 | | - layout.addWidget(build_group) |
191 | | - layout.addWidget(output_group) |
192 | | - layout.addWidget(assets_group) |
193 | | - layout.addWidget(hint) |
194 | | - layout.addStretch() |
195 | | - |
196 | | - # Store references in the engine instance for build_command access |
197 | | - self._gui = gui |
198 | | - |
199 | | - return tab, "Nuitka" |
200 | | - |
201 | | - except Exception as e: |
202 | | - try: |
203 | | - if hasattr(gui, "log"): |
204 | | - log_with_level(gui, "error", f"Erreur création onglet Nuitka: {e}") |
205 | | - except Exception: |
206 | | - pass |
207 | | - return None |
208 | | - |
209 | | - def get_config(self, gui) -> dict: |
210 | | - """Return a JSON-serializable snapshot of current Nuitka UI options.""" |
211 | | - try: |
212 | | - cfg = {} |
213 | | - if hasattr(self, "_nuitka_onefile") and self._nuitka_onefile is not None: |
214 | | - cfg["onefile"] = bool(self._nuitka_onefile.isChecked()) |
215 | | - if ( |
216 | | - hasattr(self, "_nuitka_standalone") |
217 | | - and self._nuitka_standalone is not None |
218 | | - ): |
219 | | - cfg["standalone"] = bool(self._nuitka_standalone.isChecked()) |
220 | | - if ( |
221 | | - hasattr(self, "_nuitka_disable_console") |
222 | | - and self._nuitka_disable_console is not None |
223 | | - ): |
224 | | - cfg["disable_console"] = bool(self._nuitka_disable_console.isChecked()) |
225 | | - if ( |
226 | | - hasattr(self, "_nuitka_output_dir") |
227 | | - and self._nuitka_output_dir is not None |
228 | | - ): |
229 | | - cfg["output_dir"] = self._nuitka_output_dir.text().strip() |
230 | | - icon_path = "" |
231 | | - if ( |
232 | | - hasattr(self, "_nuitka_icon_path_input") |
233 | | - and self._nuitka_icon_path_input is not None |
234 | | - ): |
235 | | - icon_path = self._nuitka_icon_path_input.text().strip() |
236 | | - if ( |
237 | | - not icon_path |
238 | | - and hasattr(self, "_nuitka_selected_icon") |
239 | | - and self._nuitka_selected_icon |
240 | | - ): |
241 | | - icon_path = str(self._nuitka_selected_icon).strip() |
242 | | - if ( |
243 | | - not icon_path |
244 | | - and hasattr(self, "_selected_icon") |
245 | | - and self._selected_icon |
246 | | - ): |
247 | | - icon_path = str(self._selected_icon).strip() |
248 | | - if icon_path: |
249 | | - self._nuitka_selected_icon = icon_path |
250 | | - self._selected_icon = icon_path |
251 | | - cfg["selected_icon"] = icon_path |
252 | | - return cfg |
253 | | - except Exception: |
254 | | - return {} |
255 | | - |
256 | | - def set_config(self, gui, cfg: dict) -> None: |
257 | | - """Apply a config dict to Nuitka UI widgets.""" |
258 | | - if not isinstance(cfg, dict): |
259 | | - return |
260 | | - try: |
261 | | - if ( |
262 | | - hasattr(self, "_nuitka_onefile") |
263 | | - and self._nuitka_onefile is not None |
264 | | - and "onefile" in cfg |
265 | | - ): |
266 | | - self._nuitka_onefile.setChecked(bool(cfg.get("onefile"))) |
267 | | - if ( |
268 | | - hasattr(self, "_nuitka_standalone") |
269 | | - and self._nuitka_standalone is not None |
270 | | - and "standalone" in cfg |
271 | | - ): |
272 | | - self._nuitka_standalone.setChecked(bool(cfg.get("standalone"))) |
273 | | - if ( |
274 | | - hasattr(self, "_nuitka_disable_console") |
275 | | - and self._nuitka_disable_console is not None |
276 | | - and "disable_console" in cfg |
277 | | - ): |
278 | | - self._nuitka_disable_console.setChecked( |
279 | | - bool(cfg.get("disable_console")) |
280 | | - ) |
281 | | - if ( |
282 | | - hasattr(self, "_nuitka_output_dir") |
283 | | - and self._nuitka_output_dir is not None |
284 | | - and "output_dir" in cfg |
285 | | - ): |
286 | | - val = cfg.get("output_dir") or "" |
287 | | - self._nuitka_output_dir.setText(str(val)) |
288 | | - if "selected_icon" in cfg: |
289 | | - icon = cfg.get("selected_icon") or "" |
290 | | - self._nuitka_selected_icon = icon or None |
291 | | - self._selected_icon = icon or None |
292 | | - if ( |
293 | | - hasattr(self, "_nuitka_icon_path_input") |
294 | | - and self._nuitka_icon_path_input is not None |
295 | | - ): |
296 | | - self._nuitka_icon_path_input.setText(str(icon)) |
297 | | - except Exception: |
298 | | - pass |
299 | | - |
300 | | - def _get_btn(self, name: str): |
301 | | - """Get button widget from engine instance or GUI.""" |
302 | | - if hasattr(self, f"_btn_{name}"): |
303 | | - return getattr(self, f"_btn_{name}") |
304 | | - return getattr(self._gui, name, None) if hasattr(self, "_gui") else None |
305 | | - |
306 | | - def get_log_prefix(self, file_basename: str) -> str: |
307 | | - return f"Nuitka ({self.version})" |
308 | | - |
309 | | - def apply_i18n(self, gui, tr: dict) -> None: |
310 | | - """Apply internationalization translations to the engine UI.""" |
311 | | - try: |
312 | | - # Apply translations to UI elements if they exist |
313 | | - if hasattr(self, "_nuitka_onefile"): |
314 | | - self._nuitka_onefile.setText( |
315 | | - self.engine_translate("onefile_checkbox", "Onefile") |
316 | | - ) |
317 | | - if hasattr(self, "_nuitka_standalone"): |
318 | | - self._nuitka_standalone.setText( |
319 | | - self.engine_translate("standalone_checkbox", "Standalone") |
320 | | - ) |
321 | | - if hasattr(self, "_nuitka_disable_console"): |
322 | | - self._nuitka_disable_console.setText( |
323 | | - self.engine_translate( |
324 | | - "disable_console_checkbox", "Disable console" |
325 | | - ) |
326 | | - ) |
327 | | - if hasattr(self, "_nuitka_disable_console"): |
328 | | - self._nuitka_disable_console.setToolTip( |
329 | | - self.engine_translate("tt_disable_console", "") |
330 | | - ) |
331 | | - if hasattr(self, "_nuitka_output_dir"): |
332 | | - self._nuitka_output_dir.setPlaceholderText( |
333 | | - self.engine_translate("output_placeholder", "Output directory") |
334 | | - ) |
335 | | - if hasattr(self, "_btn_nuitka_icon"): |
336 | | - self._btn_nuitka_icon.setText( |
337 | | - self.engine_translate("icon_button", "Select icon") |
338 | | - ) |
339 | | - except Exception: |
340 | | - pass |
341 | | - |
342 | | - def _on_icon_path_changed(self, text: str) -> None: |
343 | | - """Keep the selected icon path in sync with manual edits.""" |
344 | | - icon = text.strip() |
345 | | - self._nuitka_selected_icon = icon or None |
346 | | - self._selected_icon = icon or None |
347 | | - |
348 | | - def select_icon(self) -> None: |
349 | | - """Select an icon file for the executable.""" |
350 | | - try: |
351 | | - from PySide6.QtWidgets import QFileDialog |
352 | | - |
353 | | - file_path, _ = QFileDialog.getOpenFileName( |
354 | | - self._gui, |
355 | | - "Sélectionner une icône", |
356 | | - "", |
357 | | - "Fichiers icône (*.ico);;Tous les fichiers (*)", |
358 | | - ) |
359 | | - if file_path: |
360 | | - self._selected_icon = file_path |
361 | | - self._nuitka_selected_icon = file_path |
362 | | - if ( |
363 | | - hasattr(self, "_nuitka_icon_path_input") |
364 | | - and self._nuitka_icon_path_input is not None |
365 | | - ): |
366 | | - self._nuitka_icon_path_input.setText(file_path) |
367 | | - if hasattr(self._gui, "log"): |
368 | | - self._gui.log.append( |
369 | | - f"Icône sélectionnée pour Nuitka : {file_path}" |
370 | | - ) |
371 | | - except Exception as e: |
372 | | - if hasattr(self._gui, "log"): |
373 | | - log_with_level( |
374 | | - self._gui, "error", f"Erreur lors de la sélection de l'icône : {e}" |
375 | | - ) |
| 37 | +_auto_import_all() |
0 commit comments