@@ -372,7 +372,6 @@ def json_report(self, filename, version="NA", platform=None, filters=None):
372372 suite ["available_rom" ] = available_rom
373373 if instance .status in [TwisterStatus .ERROR , TwisterStatus .FAIL ]:
374374 suite ['status' ] = instance .status
375- suite ["reason" ] = instance .reason
376375 # FIXME
377376 if os .path .exists (pytest_log ):
378377 suite ["log" ] = self .process_log (pytest_log )
@@ -382,6 +381,11 @@ def json_report(self, filename, version="NA", platform=None, filters=None):
382381 suite ["log" ] = self .process_log (device_log )
383382 else :
384383 suite ["log" ] = self .process_log (build_log )
384+
385+ suite ["reason" ] = self .get_detailed_reason (instance .reason , suite ["log" ])
386+ # update the reason to get more details also in other reports (e.g. junit)
387+ # where build log is not available
388+ instance .reason = suite ["reason" ]
385389 elif instance .status == TwisterStatus .FILTER :
386390 suite ["status" ] = TwisterStatus .FILTER
387391 suite ["reason" ] = instance .reason
@@ -798,3 +802,55 @@ def target_report(self, json_file, outdir, suffix):
798802 self .json_report (json_platform_file + "_footprint.json" ,
799803 version = self .env .version , platform = platform .name ,
800804 filters = self .json_filters ['footprint.json' ])
805+
806+ def get_detailed_reason (self , reason : str , log : str ) -> str :
807+ if reason == 'CMake build failure' :
808+ if error_key := self ._parse_cmake_build_failure (log ):
809+ return f"{ reason } - { error_key } "
810+ elif reason == 'Build failure' : # noqa SIM102
811+ if error_key := self ._parse_build_failure (log ):
812+ return f"{ reason } - { error_key } "
813+ return reason
814+
815+ @staticmethod
816+ def _parse_cmake_build_failure (log : str ) -> str | None :
817+ last_warning = 'no warning found'
818+ lines = log .splitlines ()
819+ for i , line in enumerate (lines ):
820+ if "warning: " in line :
821+ last_warning = line
822+ elif "devicetree error: " in line :
823+ return "devicetree error"
824+ elif "fatal error: " in line :
825+ return line [line .index ('fatal error: ' ) :].strip ()
826+ elif "error: " in line : # error: Aborting due to Kconfig warnings
827+ if "undefined symbol" in last_warning :
828+ return last_warning [last_warning .index ('undefined symbol' ) :].strip ()
829+ return last_warning
830+ elif "CMake Error at" in line :
831+ for next_line in lines [i + 1 :]:
832+ if next_line .strip ():
833+ return line + ' ' + next_line
834+ return line
835+ return None
836+
837+ @staticmethod
838+ def _parse_build_failure (log : str ) -> str | None :
839+ last_warning = ''
840+ lines = log .splitlines ()
841+ for i , line in enumerate (lines ):
842+ if "undefined reference" in line :
843+ return line [line .index ('undefined reference' ) :].strip ()
844+ elif "error: ld returned" in line :
845+ if last_warning :
846+ return last_warning
847+ elif "overflowed by" in lines [i - 1 ]:
848+ return "ld.bfd: region overflowed"
849+ elif "ld.bfd: warning: " in lines [i - 1 ]:
850+ return "ld.bfd:" + lines [i - 1 ].split ("ld.bfd:" , 1 )[- 1 ]
851+ return line
852+ elif "error: " in line :
853+ return line [line .index ('error: ' ) :].strip ()
854+ elif ": in function " in line :
855+ last_warning = line [line .index ('in function' ) :].strip ()
856+ return None
0 commit comments