Coverage for src/ezcompiler/utils/_compiler_utils.py: 100.00%
33 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-04 11:22 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-04 11:22 +0000
1# ///////////////////////////////////////////////////////////////
2# COMPILER_UTILS - Compiler-specific utility functions
3# Project: ezcompiler
4# ///////////////////////////////////////////////////////////////
6"""
7Compiler utilities - Compiler-specific utility functions for EzCompiler.
9This module provides specialized utility functions for compiler operations,
10including configuration validation, output directory preparation, and include
11files formatting. Uses thematic utils (FileUtils, ValidationUtils) internally.
13Utils layer can only use DEBUG and ERROR log levels.
14"""
16from __future__ import annotations
18# ///////////////////////////////////////////////////////////////
19# IMPORTS
20# ///////////////////////////////////////////////////////////////
21# Standard library imports
22from pathlib import Path
24# Local imports
25from ..shared import CompilerConfig
26from ..shared.exceptions.utils import (
27 CompilerConfigValidationError,
28 MainFileNotFoundError,
29 OutputDirectoryError,
30)
31from ._file_utils import FileUtils
33# ///////////////////////////////////////////////////////////////
34# CLASSES
35# ///////////////////////////////////////////////////////////////
38class CompilerUtils:
39 """
40 Utility class for compiler-specific operations.
42 Provides static methods for compiler-related tasks such as configuration
43 validation, output directory preparation, and include files formatting.
44 Uses thematic utils (FileUtils, ValidationUtils) internally.
46 Example:
47 >>> config = CompilerConfig(...)
48 >>> CompilerUtils.validate_compiler_config(config)
49 >>> CompilerUtils.prepare_compiler_output_directory(config)
50 >>> files = CompilerUtils.format_include_files_data(config)
51 """
53 # ////////////////////////////////////////////////
54 # VALIDATION METHODS
55 # ////////////////////////////////////////////////
57 # Revalidation volontaire (défense en profondeur) : CompilerConfig est une dataclass
58 # mutable, donc l'état validé par __post_init__ peut avoir changé entre la construction
59 # et l'appel à compile(). Ce contrôle est rejoué juste avant la compilation — ce n'est
60 # pas une duplication morte mais un garde-fou au point d'usage.
61 @staticmethod
62 def validate_compiler_config(config: CompilerConfig) -> None:
63 """
64 Validate configuration for compilation.
66 Checks that all required configuration fields are present
67 and valid (main file exists, output folder is set).
69 Args:
70 config: CompilerConfig instance to validate
72 Raises:
73 MainFileNotFoundError: If main file doesn't exist or is required but missing
74 OutputDirectoryError: If output folder is not set
76 Note:
77 Uses FileUtils for file existence checks.
79 Example:
80 >>> config = CompilerConfig(...)
81 >>> CompilerUtils.validate_compiler_config(config)
82 """
83 if not config.main_file:
84 raise CompilerConfigValidationError("Main file is required")
86 main_file_path = Path(config.main_file)
87 if not main_file_path.exists() or not main_file_path.is_file():
88 raise MainFileNotFoundError(f"Main file not found: {config.main_file}")
90 if not config.output_folder:
91 raise OutputDirectoryError("Output folder is required")
93 # ////////////////////////////////////////////////
94 # PREPARATION METHODS
95 # ////////////////////////////////////////////////
97 @staticmethod
98 def prepare_compiler_output_directory(config: CompilerConfig) -> None:
99 """
100 Prepare the output directory for compilation.
102 Creates the output directory if it doesn't exist, including
103 any parent directories as needed.
105 Args:
106 config: CompilerConfig instance with output_folder path
108 Note:
109 Uses FileUtils.create_directory_if_not_exists() internally.
111 Example:
112 >>> config = CompilerConfig(..., output_folder=Path("dist"))
113 >>> CompilerUtils.prepare_compiler_output_directory(config)
114 """
115 FileUtils.create_directory_if_not_exists(config.output_folder)
117 # ////////////////////////////////////////////////
118 # DATA FORMATTING METHODS
119 # ////////////////////////////////////////////////
121 @staticmethod
122 def format_include_files_data(config: CompilerConfig) -> list[str]:
123 """
124 Format include files data for compilation.
126 Combines individual files and folders from configuration,
127 formatting folders with trailing slashes for compatibility
128 with different compilers.
130 Args:
131 config: CompilerConfig instance with include_files
133 Returns:
134 list[str]: List of formatted include paths
136 Note:
137 Files are included as-is, folders are formatted with
138 trailing slashes for Cx_Freeze compatibility.
140 Example:
141 >>> config.include_files = {
142 ... "files": ["config.yaml"],
143 ... "folders": ["lib", "assets"]
144 ... }
145 >>> files = CompilerUtils.format_include_files_data(config)
146 >>> print(files)
147 ['config.yaml', 'lib/', 'assets/']
148 """
149 files = config.include_files.get("files", [])
150 folders = [f"{folder}/" for folder in config.include_files.get("folders", [])]
151 return files + folders
153 # ////////////////////////////////////////////////
154 # COMPILER-SPECIFIC HELPERS
155 # ////////////////////////////////////////////////
157 @staticmethod
158 def get_windows_base_for_console(console: bool) -> str | None:
159 """
160 Get Windows base executable type based on console setting.
162 Args:
163 console: Whether to show console window
165 Returns:
166 str | None: "Win32GUI" if console=False on Windows, None otherwise
168 Note:
169 Used by Cx_Freeze to determine executable base type.
171 Example:
172 >>> base = CompilerUtils.get_windows_base_for_console(False)
173 >>> print(base)
174 'Win32GUI' # On Windows
175 """
176 import sys
178 if sys.platform == "win32" and not console:
179 return "Win32GUI"
180 return None
182 @staticmethod
183 def check_onefile_mode() -> bool:
184 """
185 Check if onefile mode is requested via command-line arguments.
187 Returns:
188 bool: True if --onefile is in sys.argv, False otherwise
190 Note:
191 Used by PyInstaller and Nuitka to determine output format.
193 Example:
194 >>> # When run with: python script.py --onefile
195 >>> is_onefile = CompilerUtils.check_onefile_mode()
196 >>> print(is_onefile)
197 True
198 """
199 import sys
201 return "--onefile" in sys.argv