Commit cac967db authored by Grzegorz Chwierut's avatar Grzegorz Chwierut Committed by Benjamin Cabé
Browse files

twister: extend reason field in Twister reports



Extended the reason field in Twister report to include
more detailed information for 'Build failure' and
'CMake build failure'

Signed-off-by: default avatarGrzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
parent 0bb7849a
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -372,7 +372,6 @@ class Reporting:
                suite["available_rom"] = available_rom
            if instance.status in [TwisterStatus.ERROR, TwisterStatus.FAIL]:
                suite['status'] = instance.status
                suite["reason"] = instance.reason
                # FIXME
                if os.path.exists(pytest_log):
                    suite["log"] = self.process_log(pytest_log)
@@ -382,6 +381,11 @@ class Reporting:
                    suite["log"] = self.process_log(device_log)
                else:
                    suite["log"] = self.process_log(build_log)

                suite["reason"] = self.get_detailed_reason(instance.reason, suite["log"])
                # update the reason to get more details also in other reports (e.g. junit)
                # where build log is not available
                instance.reason = suite["reason"]
            elif instance.status == TwisterStatus.FILTER:
                suite["status"] = TwisterStatus.FILTER
                suite["reason"] = instance.reason
@@ -798,3 +802,55 @@ class Reporting:
                self.json_report(json_platform_file + "_footprint.json",
                                 version=self.env.version, platform=platform.name,
                                 filters=self.json_filters['footprint.json'])

    def get_detailed_reason(self, reason: str, log: str) -> str:
        if reason == 'CMake build failure':
            if error_key := self._parse_cmake_build_failure(log):
                return f"{reason} - {error_key}"
        elif reason == 'Build failure':  # noqa SIM102
            if error_key := self._parse_build_failure(log):
                return f"{reason} - {error_key}"
        return reason

    @staticmethod
    def _parse_cmake_build_failure(log: str) -> str | None:
        last_warning = 'no warning found'
        lines = log.splitlines()
        for i, line in enumerate(lines):
            if "warning: " in line:
                last_warning = line
            elif "devicetree error: " in line:
                return "devicetree error"
            elif "fatal error: " in line:
                return line[line.index('fatal error: ') :].strip()
            elif "error: " in line:  # error: Aborting due to Kconfig warnings
                if "undefined symbol" in last_warning:
                    return last_warning[last_warning.index('undefined symbol') :].strip()
                return last_warning
            elif "CMake Error at" in line:
                for next_line in lines[i + 1 :]:
                    if next_line.strip():
                        return line + ' ' + next_line
                return line
        return None

    @staticmethod
    def _parse_build_failure(log: str) -> str | None:
        last_warning = ''
        lines = log.splitlines()
        for i, line in enumerate(lines):
            if "undefined reference" in line:
                return line[line.index('undefined reference') :].strip()
            elif "error: ld returned" in line:
                if last_warning:
                    return last_warning
                elif "overflowed by" in lines[i - 1]:
                    return "ld.bfd: region overflowed"
                elif "ld.bfd: warning: " in lines[i - 1]:
                    return "ld.bfd:" + lines[i - 1].split("ld.bfd:", 1)[-1]
                return line
            elif "error: " in line:
                return line[line.index('error: ') :].strip()
            elif ": in function " in line:
                last_warning = line[line.index('in function') :].strip()
        return None