@@ -22,13 +22,15 @@ class CompilationResult:
2222 """Representa el resultado de compilar un archivo."""
2323
2424 def __init__ (self , file_path : str , success : bool , duration : float ,
25- stdout : str = "" , stderr : str = "" , error_message : str = "" ):
25+ stdout : str = "" , stderr : str = "" , error_message : str = "" ,
26+ error_type : str = "" ):
2627 self .file_path = file_path
2728 self .success = success
2829 self .duration = duration
2930 self .stdout = stdout
3031 self .stderr = stderr
3132 self .error_message = error_message
33+ self .error_type = error_type # 'config', 'code', 'dependency', 'unknown'
3234
3335 def to_dict (self ) -> dict :
3436 """Convierte el resultado a un diccionario para JSON."""
@@ -38,10 +40,99 @@ def to_dict(self) -> dict:
3840 "duration" : self .duration ,
3941 "stdout" : self .stdout ,
4042 "stderr" : self .stderr ,
41- "error_message" : self .error_message
43+ "error_message" : self .error_message ,
44+ "error_type" : self .error_type
4245 }
4346
4447
48+ def ensure_kernel_configured (kernel_root : Path ) -> bool :
49+ """
50+ Verifica que el kernel esté configurado y lo configura si es necesario.
51+
52+ Args:
53+ kernel_root: Directorio raíz del kernel
54+
55+ Returns:
56+ True si el kernel está configurado correctamente
57+ """
58+ config_file = kernel_root / ".config"
59+
60+ # Si ya existe .config, asumir que está configurado
61+ if config_file .exists ():
62+ return True
63+
64+ print ("[COMPILE] Kernel not configured. Running 'make defconfig'..." )
65+ try :
66+ result = subprocess .run (
67+ ['make' , 'defconfig' ],
68+ cwd = str (kernel_root ),
69+ capture_output = True ,
70+ text = True ,
71+ timeout = 60
72+ )
73+
74+ if result .returncode == 0 and config_file .exists ():
75+ print ("[COMPILE] ✓ Kernel configured successfully" )
76+ return True
77+ else :
78+ print (f"[COMPILE] ✗ Failed to configure kernel: { result .stderr [:200 ]} " )
79+ return False
80+
81+ except Exception as e :
82+ print (f"[COMPILE] ✗ Exception while configuring kernel: { e } " )
83+ return False
84+
85+
86+ def classify_compilation_error (error_msg : str , stderr : str ) -> str :
87+ """
88+ Clasifica el tipo de error de compilación para ayudar al diagnóstico.
89+
90+ Returns:
91+ Clasificación del error: 'config', 'code', 'dependency', 'unknown'
92+ """
93+ error_lower = (error_msg + "\n " + stderr ).lower ()
94+
95+ # Errores de configuración del kernel (CONFIG_* no definido)
96+ config_indicators = [
97+ 'undeclared' ,
98+ 'not declared in this scope' ,
99+ 'was not declared' ,
100+ 'implicit declaration' ,
101+ 'redefinition' , # A veces por #ifdef CONFIG_
102+ ]
103+
104+ # Errores de dependencia/header faltante
105+ dependency_indicators = [
106+ 'no such file or directory' ,
107+ 'cannot find' ,
108+ '#include' ,
109+ ]
110+
111+ # Errores de código real (sintaxis, tipos, etc.)
112+ code_indicators = [
113+ 'syntax error' ,
114+ 'expected' ,
115+ 'conflicting types' ,
116+ 'incompatible' ,
117+ 'invalid' ,
118+ ]
119+
120+ # Clasificar
121+ for indicator in dependency_indicators :
122+ if indicator in error_lower :
123+ return 'dependency'
124+
125+ for indicator in config_indicators :
126+ if indicator in error_lower :
127+ return 'config'
128+
129+ for indicator in code_indicators :
130+ if indicator in error_lower :
131+ return 'code'
132+
133+ return 'unknown'
134+
135+
45136def compile_single_file (file_path : Path , kernel_root : Path ) -> CompilationResult :
46137 """
47138 Compila un archivo individual del kernel Linux usando el sistema de build del kernel.
@@ -74,6 +165,7 @@ def compile_single_file(file_path: Path, kernel_root: Path) -> CompilationResult
74165
75166 success = result .returncode == 0
76167 error_msg = ""
168+ error_type = ""
77169
78170 if not success :
79171 # Extraer mensaje de error más relevante
@@ -84,14 +176,18 @@ def compile_single_file(file_path: Path, kernel_root: Path) -> CompilationResult
84176 error_msg = '\n ' .join (error_msgs [:5 ]) # Primeros 5 errores
85177 else :
86178 error_msg = result .stderr [:500 ] # Primeros 500 chars si no hay errores explícitos
179+
180+ # Clasificar el tipo de error
181+ error_type = classify_compilation_error (error_msg , result .stderr )
87182
88183 return CompilationResult (
89184 file_path = str (file_path ),
90185 success = success ,
91186 duration = duration ,
92187 stdout = result .stdout ,
93188 stderr = result .stderr ,
94- error_message = error_msg
189+ error_message = error_msg ,
190+ error_type = error_type
95191 )
96192
97193 except subprocess .TimeoutExpired :
@@ -156,6 +252,11 @@ def compile_modified_files(files: List[Path], kernel_root: Path,
156252 """
157253 results = []
158254
255+ # Verificar/configurar el kernel antes de compilar
256+ if not ensure_kernel_configured (kernel_root ):
257+ print ("[COMPILE] ✗ Cannot compile without kernel configuration" )
258+ return results
259+
159260 print (f"[COMPILE] Compilando { len (files )} archivos..." )
160261
161262 for i , file_path in enumerate (files , 1 ):
@@ -224,13 +325,20 @@ def summarize_results(results: List[CompilationResult]) -> Dict:
224325 total_duration = sum (r .duration for r in results )
225326 avg_duration = total_duration / total if total > 0 else 0
226327
328+ # Clasificar errores por tipo
329+ error_types = {}
330+ for r in results :
331+ if not r .success and r .error_type :
332+ error_types [r .error_type ] = error_types .get (r .error_type , 0 ) + 1
333+
227334 return {
228335 "total" : total ,
229336 "successful" : successful ,
230337 "failed" : failed ,
231338 "success_rate" : (successful / total * 100 ) if total > 0 else 0 ,
232339 "total_duration" : total_duration ,
233- "avg_duration" : avg_duration
340+ "avg_duration" : avg_duration ,
341+ "error_types" : error_types
234342 }
235343
236344
@@ -253,11 +361,25 @@ def print_summary(results: List[CompilationResult]):
253361 print (f"Tiempo promedio: { summary ['avg_duration' ]:.2f} s" )
254362 print ("=" * 60 )
255363
364+ # Mostrar clasificación de errores si hay fallos
365+ if summary .get ('error_types' ):
366+ print ("\n Clasificación de errores:" )
367+ error_labels = {
368+ 'config' : 'Config/Context (símbolos no declarados por CONFIG_*)' ,
369+ 'dependency' : 'Dependencies (headers/archivos faltantes)' ,
370+ 'code' : 'Código (sintaxis, tipos, etc.)' ,
371+ 'unknown' : 'Desconocido'
372+ }
373+ for error_type , count in summary ['error_types' ].items ():
374+ label = error_labels .get (error_type , error_type )
375+ print (f" • { label } : { count } " )
376+
256377 if summary ['failed' ] > 0 :
257378 print ("\n Archivos con errores de compilación:" )
258379 for result in results :
259380 if not result .success :
260- print (f" ✗ { Path (result .file_path ).name } " )
381+ error_type_label = f" [{ result .error_type } ]" if result .error_type else ""
382+ print (f" ✗ { Path (result .file_path ).name } { error_type_label } " )
261383 if result .error_message :
262384 # Mostrar primera línea de error
263385 first_line = result .error_message .split ('\n ' )[0 ]
0 commit comments