Coverage for src / ezcompiler / utils / compiler_utils.py: 39.02%

33 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-27 06:49 +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.compiler_config 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 @staticmethod 

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

59 """ 

60 Validate configuration for compilation. 

61 

62 Checks that all required configuration fields are present 

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

64 

65 Args: 

66 config: CompilerConfig instance to validate 

67 

68 Raises: 

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

70 OutputDirectoryError: If output folder is not set 

71 

72 Note: 

73 Uses FileUtils for file existence checks. 

74 

75 Example: 

76 >>> config = CompilerConfig(...) 

77 >>> CompilerUtils.validate_compiler_config(config) 

78 """ 

79 if not config.main_file: 

80 raise CompilerConfigValidationError("Main file is required") 

81 

82 main_file_path = Path(config.main_file) 

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

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

85 

86 if not config.output_folder: 

87 raise OutputDirectoryError("Output folder is required") 

88 

89 # //////////////////////////////////////////////// 

90 # PREPARATION METHODS 

91 # //////////////////////////////////////////////// 

92 

93 @staticmethod 

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

95 """ 

96 Prepare the output directory for compilation. 

97 

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

99 any parent directories as needed. 

100 

101 Args: 

102 config: CompilerConfig instance with output_folder path 

103 

104 Note: 

105 Uses FileUtils.create_directory_if_not_exists() internally. 

106 

107 Example: 

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

109 >>> CompilerUtils.prepare_compiler_output_directory(config) 

110 """ 

111 FileUtils.create_directory_if_not_exists(config.output_folder) 

112 

113 # //////////////////////////////////////////////// 

114 # DATA FORMATTING METHODS 

115 # //////////////////////////////////////////////// 

116 

117 @staticmethod 

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

119 """ 

120 Format include files data for compilation. 

121 

122 Combines individual files and folders from configuration, 

123 formatting folders with trailing slashes for compatibility 

124 with different compilers. 

125 

126 Args: 

127 config: CompilerConfig instance with include_files 

128 

129 Returns: 

130 list[str]: List of formatted include paths 

131 

132 Note: 

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

134 trailing slashes for Cx_Freeze compatibility. 

135 

136 Example: 

137 >>> config.include_files = { 

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

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

140 ... } 

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

142 >>> print(files) 

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

144 """ 

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

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

147 return files + folders 

148 

149 # //////////////////////////////////////////////// 

150 # COMPILER-SPECIFIC HELPERS 

151 # //////////////////////////////////////////////// 

152 

153 @staticmethod 

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

155 """ 

156 Get Windows base executable type based on console setting. 

157 

158 Args: 

159 console: Whether to show console window 

160 

161 Returns: 

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

163 

164 Note: 

165 Used by Cx_Freeze to determine executable base type. 

166 

167 Example: 

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

169 >>> print(base) 

170 'Win32GUI' # On Windows 

171 """ 

172 import sys 

173 

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

175 return "Win32GUI" 

176 return None 

177 

178 @staticmethod 

179 def check_onefile_mode() -> bool: 

180 """ 

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

182 

183 Returns: 

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

185 

186 Note: 

187 Used by PyInstaller and Nuitka to determine output format. 

188 

189 Example: 

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

191 >>> is_onefile = CompilerUtils.check_onefile_mode() 

192 >>> print(is_onefile) 

193 True 

194 """ 

195 import sys 

196 

197 return "--onefile" in sys.argv