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

1# /////////////////////////////////////////////////////////////// 

2# COMPILER_UTILS - Compiler-specific utility functions 

3# Project: ezcompiler 

4# /////////////////////////////////////////////////////////////// 

5 

6""" 

7Compiler utilities - Compiler-specific utility functions for EzCompiler. 

8 

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. 

12 

13Utils layer can only use DEBUG and ERROR log levels. 

14""" 

15 

16from __future__ import annotations 

17 

18# /////////////////////////////////////////////////////////////// 

19# IMPORTS 

20# /////////////////////////////////////////////////////////////// 

21# Standard library imports 

22from pathlib import Path 

23 

24# Local imports 

25from ..shared import CompilerConfig 

26from ..shared.exceptions.utils import ( 

27 CompilerConfigValidationError, 

28 MainFileNotFoundError, 

29 OutputDirectoryError, 

30) 

31from ._file_utils import FileUtils 

32 

33# /////////////////////////////////////////////////////////////// 

34# CLASSES 

35# /////////////////////////////////////////////////////////////// 

36 

37 

38class CompilerUtils: 

39 """ 

40 Utility class for compiler-specific operations. 

41 

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. 

45 

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 """ 

52 

53 # //////////////////////////////////////////////// 

54 # VALIDATION METHODS 

55 # //////////////////////////////////////////////// 

56 

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. 

65 

66 Checks that all required configuration fields are present 

67 and valid (main file exists, output folder is set). 

68 

69 Args: 

70 config: CompilerConfig instance to validate 

71 

72 Raises: 

73 MainFileNotFoundError: If main file doesn't exist or is required but missing 

74 OutputDirectoryError: If output folder is not set 

75 

76 Note: 

77 Uses FileUtils for file existence checks. 

78 

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") 

85 

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}") 

89 

90 if not config.output_folder: 

91 raise OutputDirectoryError("Output folder is required") 

92 

93 # //////////////////////////////////////////////// 

94 # PREPARATION METHODS 

95 # //////////////////////////////////////////////// 

96 

97 @staticmethod 

98 def prepare_compiler_output_directory(config: CompilerConfig) -> None: 

99 """ 

100 Prepare the output directory for compilation. 

101 

102 Creates the output directory if it doesn't exist, including 

103 any parent directories as needed. 

104 

105 Args: 

106 config: CompilerConfig instance with output_folder path 

107 

108 Note: 

109 Uses FileUtils.create_directory_if_not_exists() internally. 

110 

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) 

116 

117 # //////////////////////////////////////////////// 

118 # DATA FORMATTING METHODS 

119 # //////////////////////////////////////////////// 

120 

121 @staticmethod 

122 def format_include_files_data(config: CompilerConfig) -> list[str]: 

123 """ 

124 Format include files data for compilation. 

125 

126 Combines individual files and folders from configuration, 

127 formatting folders with trailing slashes for compatibility 

128 with different compilers. 

129 

130 Args: 

131 config: CompilerConfig instance with include_files 

132 

133 Returns: 

134 list[str]: List of formatted include paths 

135 

136 Note: 

137 Files are included as-is, folders are formatted with 

138 trailing slashes for Cx_Freeze compatibility. 

139 

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 

152 

153 # //////////////////////////////////////////////// 

154 # COMPILER-SPECIFIC HELPERS 

155 # //////////////////////////////////////////////// 

156 

157 @staticmethod 

158 def get_windows_base_for_console(console: bool) -> str | None: 

159 """ 

160 Get Windows base executable type based on console setting. 

161 

162 Args: 

163 console: Whether to show console window 

164 

165 Returns: 

166 str | None: "Win32GUI" if console=False on Windows, None otherwise 

167 

168 Note: 

169 Used by Cx_Freeze to determine executable base type. 

170 

171 Example: 

172 >>> base = CompilerUtils.get_windows_base_for_console(False) 

173 >>> print(base) 

174 'Win32GUI' # On Windows 

175 """ 

176 import sys 

177 

178 if sys.platform == "win32" and not console: 

179 return "Win32GUI" 

180 return None 

181 

182 @staticmethod 

183 def check_onefile_mode() -> bool: 

184 """ 

185 Check if onefile mode is requested via command-line arguments. 

186 

187 Returns: 

188 bool: True if --onefile is in sys.argv, False otherwise 

189 

190 Note: 

191 Used by PyInstaller and Nuitka to determine output format. 

192 

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 

200 

201 return "--onefile" in sys.argv