Coverage for src/ezcompiler/utils/_compiler_utils.py: 100.00%
33 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 16:37 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 16:37 +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 # Deliberate revalidation (defense in depth): CompilerConfig is a mutable
58 # dataclass, so the state validated by __post_init__ may have changed between
59 # construction and the call to compile(). This check is replayed right before
60 # the compilation — it is not
61 # pas une duplication morte mais un garde-fou au point d'usage.
62 @staticmethod
63 def validate_compiler_config(config: CompilerConfig) -> None:
64 """
65 Validate configuration for compilation.
67 Checks that all required configuration fields are present
68 and valid (main file exists, output folder is set).
70 Args:
71 config: CompilerConfig instance to validate
73 Raises:
74 MainFileNotFoundError: If main file doesn't exist or is required but missing
75 OutputDirectoryError: If output folder is not set
77 Note:
78 Uses FileUtils for file existence checks.
80 Example:
81 >>> config = CompilerConfig(...)
82 >>> CompilerUtils.validate_compiler_config(config)
83 """
84 if not config.main_file:
85 raise CompilerConfigValidationError("Main file is required")
87 main_file_path = Path(config.main_file)
88 if not main_file_path.exists() or not main_file_path.is_file():
89 raise MainFileNotFoundError(f"Main file not found: {config.main_file}")
91 if not config.output_folder:
92 raise OutputDirectoryError("Output folder is required")
94 # ////////////////////////////////////////////////
95 # PREPARATION METHODS
96 # ////////////////////////////////////////////////
98 @staticmethod
99 def prepare_compiler_output_directory(config: CompilerConfig) -> None:
100 """
101 Prepare the output directory for compilation.
103 Creates the output directory if it doesn't exist, including
104 any parent directories as needed.
106 Args:
107 config: CompilerConfig instance with output_folder path
109 Note:
110 Uses FileUtils.create_directory_if_not_exists() internally.
112 Example:
113 >>> config = CompilerConfig(..., output_folder=Path("dist"))
114 >>> CompilerUtils.prepare_compiler_output_directory(config)
115 """
116 FileUtils.create_directory_if_not_exists(config.output_folder)
118 # ////////////////////////////////////////////////
119 # DATA FORMATTING METHODS
120 # ////////////////////////////////////////////////
122 @staticmethod
123 def format_include_files_data(config: CompilerConfig) -> list[str]:
124 """
125 Format include files data for compilation.
127 Combines individual files and folders from configuration,
128 formatting folders with trailing slashes for compatibility
129 with different compilers.
131 Args:
132 config: CompilerConfig instance with include_files
134 Returns:
135 list[str]: List of formatted include paths
137 Note:
138 Files are included as-is, folders are formatted with
139 trailing slashes for Cx_Freeze compatibility.
141 Example:
142 >>> config.include_files = {
143 ... "files": ["config.yaml"],
144 ... "folders": ["lib", "assets"]
145 ... }
146 >>> files = CompilerUtils.format_include_files_data(config)
147 >>> print(files)
148 ['config.yaml', 'lib/', 'assets/']
149 """
150 files = config.include_files.get("files", [])
151 folders = [f"{folder}/" for folder in config.include_files.get("folders", [])]
152 return files + folders
154 # ////////////////////////////////////////////////
155 # COMPILER-SPECIFIC HELPERS
156 # ////////////////////////////////////////////////
158 @staticmethod
159 def get_windows_base_for_console(console: bool) -> str | None:
160 """
161 Get Windows base executable type based on console setting.
163 Args:
164 console: Whether to show console window
166 Returns:
167 str | None: "Win32GUI" if console=False on Windows, None otherwise
169 Note:
170 Used by Cx_Freeze to determine executable base type.
172 Example:
173 >>> base = CompilerUtils.get_windows_base_for_console(False)
174 >>> print(base)
175 'Win32GUI' # On Windows
176 """
177 import sys
179 if sys.platform == "win32" and not console:
180 return "Win32GUI"
181 return None
183 @staticmethod
184 def check_onefile_mode() -> bool:
185 """
186 Check if onefile mode is requested via command-line arguments.
188 Returns:
189 bool: True if --onefile is in sys.argv, False otherwise
191 Note:
192 Used by PyInstaller and Nuitka to determine output format.
194 Example:
195 >>> # When run with: python script.py --onefile
196 >>> is_onefile = CompilerUtils.check_onefile_mode()
197 >>> print(is_onefile)
198 True
199 """
200 import sys
202 return "--onefile" in sys.argv