Coverage for src/ezcompiler/utils/_compiler_utils.py: 100.00%

33 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-03 15:46 +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 # TODO [AUDIT P3]: cette méthode duplique la validation de CompilerConfig.__post_init__() 

58 # Un seul point de validation devrait exister — idéalement dans le domain object (__post_init__). 

59 # Évaluer si cette méthode peut être supprimée ou réduite à un appel vers CompilerConfig. 

60 @staticmethod 

61 def validate_compiler_config(config: CompilerConfig) -> None: 

62 """ 

63 Validate configuration for compilation. 

64 

65 Checks that all required configuration fields are present 

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

67 

68 Args: 

69 config: CompilerConfig instance to validate 

70 

71 Raises: 

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

73 OutputDirectoryError: If output folder is not set 

74 

75 Note: 

76 Uses FileUtils for file existence checks. 

77 

78 Example: 

79 >>> config = CompilerConfig(...) 

80 >>> CompilerUtils.validate_compiler_config(config) 

81 """ 

82 if not config.main_file: 

83 raise CompilerConfigValidationError("Main file is required") 

84 

85 main_file_path = Path(config.main_file) 

86 if not main_file_path.exists() or not main_file_path.is_file(): 

87 raise MainFileNotFoundError(f"Main file not found: {config.main_file}") 

88 

89 if not config.output_folder: 

90 raise OutputDirectoryError("Output folder is required") 

91 

92 # //////////////////////////////////////////////// 

93 # PREPARATION METHODS 

94 # //////////////////////////////////////////////// 

95 

96 @staticmethod 

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

98 """ 

99 Prepare the output directory for compilation. 

100 

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

102 any parent directories as needed. 

103 

104 Args: 

105 config: CompilerConfig instance with output_folder path 

106 

107 Note: 

108 Uses FileUtils.create_directory_if_not_exists() internally. 

109 

110 Example: 

111 >>> config = CompilerConfig(..., output_folder=Path("dist")) 

112 >>> CompilerUtils.prepare_compiler_output_directory(config) 

113 """ 

114 FileUtils.create_directory_if_not_exists(config.output_folder) 

115 

116 # //////////////////////////////////////////////// 

117 # DATA FORMATTING METHODS 

118 # //////////////////////////////////////////////// 

119 

120 @staticmethod 

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

122 """ 

123 Format include files data for compilation. 

124 

125 Combines individual files and folders from configuration, 

126 formatting folders with trailing slashes for compatibility 

127 with different compilers. 

128 

129 Args: 

130 config: CompilerConfig instance with include_files 

131 

132 Returns: 

133 list[str]: List of formatted include paths 

134 

135 Note: 

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

137 trailing slashes for Cx_Freeze compatibility. 

138 

139 Example: 

140 >>> config.include_files = { 

141 ... "files": ["config.yaml"], 

142 ... "folders": ["lib", "assets"] 

143 ... } 

144 >>> files = CompilerUtils.format_include_files_data(config) 

145 >>> print(files) 

146 ['config.yaml', 'lib/', 'assets/'] 

147 """ 

148 files = config.include_files.get("files", []) 

149 folders = [f"{folder}/" for folder in config.include_files.get("folders", [])] 

150 return files + folders 

151 

152 # //////////////////////////////////////////////// 

153 # COMPILER-SPECIFIC HELPERS 

154 # //////////////////////////////////////////////// 

155 

156 @staticmethod 

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

158 """ 

159 Get Windows base executable type based on console setting. 

160 

161 Args: 

162 console: Whether to show console window 

163 

164 Returns: 

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

166 

167 Note: 

168 Used by Cx_Freeze to determine executable base type. 

169 

170 Example: 

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

172 >>> print(base) 

173 'Win32GUI' # On Windows 

174 """ 

175 import sys 

176 

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

178 return "Win32GUI" 

179 return None 

180 

181 @staticmethod 

182 def check_onefile_mode() -> bool: 

183 """ 

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

185 

186 Returns: 

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

188 

189 Note: 

190 Used by PyInstaller and Nuitka to determine output format. 

191 

192 Example: 

193 >>> # When run with: python script.py --onefile 

194 >>> is_onefile = CompilerUtils.check_onefile_mode() 

195 >>> print(is_onefile) 

196 True 

197 """ 

198 import sys 

199 

200 return "--onefile" in sys.argv