| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- #!/usr/bin/env python
- #
- # Copyright (c) 2022 Project CHIP Authors
- # All rights reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- #
- # Execute as `python scripts/error_table.py > docs/ERROR_CODES.md` from root of repos
- #
- # This script uses heuristics scraping of the headers to generate nice tables
- #
- import re
- from dataclasses import dataclass
- from enum import IntEnum
- from operator import attrgetter
- from pathlib import Path
- @dataclass
- class ErrorCode:
- code: int
- name: str
- description: str
- @dataclass
- class ErrorDescriptor:
- section: str
- code_range: int
- # `macro_regex` needs to have `code` and `name` named groups.
- macro_regex: str
- include_description: bool = False
- class CommentState(IntEnum):
- WAIT_START_COMMENT = 0
- ACCUMULATE_COMMENT = 1
- class ErrorCodeLoader:
- def __init__(self) -> None:
- self.reset()
- def reset(self):
- self._comment_state = CommentState.WAIT_START_COMMENT
- self._last_comment = []
- self._error_codes: list[ErrorCode] = []
- def _process_comment_extract(self, line):
- if self._comment_state == CommentState.WAIT_START_COMMENT:
- if "/**" in line:
- self._last_comment = []
- self._comment_state = CommentState.ACCUMULATE_COMMENT
- elif self._comment_state == CommentState.ACCUMULATE_COMMENT:
- if "*/" in line:
- self._comment_state = CommentState.WAIT_START_COMMENT
- else:
- self._last_comment.append(line)
- def _process_error_extract(self, descriptor: ErrorDescriptor, line: str):
- match = re.search(descriptor.macro_regex, line)
- if match is None:
- return
- last_comment = "".join(self._last_comment).replace(" ", " ").replace(" ", " ").replace("*", "").replace(".", "")
- last_comment = last_comment.split("@brief")[-1].strip()
- code = int(match.group("code"), 0)
- code = descriptor.code_range | code
- name = match.group("name")
- description = last_comment if descriptor.include_description else ""
- self._error_codes.append(ErrorCode(code=code, name=name, description=description))
- def load_error_header(self, filename: Path, descriptor: ErrorDescriptor) -> list[ErrorCode]:
- self.reset()
- lines = filename.read_text().split("\n")
- for line in lines:
- line = line.strip()
- self._process_comment_extract(line)
- self._process_error_extract(descriptor, line)
- return self._error_codes
- def get_section_title(section: str) -> tuple[str, str]:
- markdown_title = f"{section} errors"
- anchor_name = f'#{markdown_title.lower().replace(" ","-").replace(".","-")}'
- return markdown_title, anchor_name
- def dump_table(header_path: Path, descriptor: ErrorDescriptor):
- loader = ErrorCodeLoader()
- codes_for_section = loader.load_error_header(header_path, descriptor)
- markdown_title, _ = get_section_title(descriptor.section)
- print(f"## {markdown_title}")
- print()
- if descriptor.include_description:
- print("| Decimal | Hex | Name | Description |")
- print("| --- | --- | --- | --- |")
- else:
- print("| Decimal | Hex | Name |")
- print("| --- | --- | --- |")
- for code in sorted(codes_for_section, key=attrgetter("code")):
- if descriptor.include_description:
- print(f"| {code.code} | 0x{code.code:02X} | `{code.name}` | {code.description} |")
- else:
- print(f"| {code.code} | 0x{code.code:02X} | `{code.name}` |")
- print()
- def main():
- descriptors = {
- "src/lib/core/CHIPError.h": ErrorDescriptor(section="SDK Core", code_range=0x000, macro_regex=r"^#define\s+(?P<name>[_A-Z0-9]+)\s+CHIP(_CORE)?_ERROR[(](?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- "src/inet/InetError.h": ErrorDescriptor(section="SDK Inet Layer", code_range=0x100, macro_regex=r"^#define\s+(?P<name>[_A-Z0-9]+)\s+CHIP_INET_ERROR[(](?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- "src/include/platform/CHIPDeviceError.h": ErrorDescriptor(section="SDK Device Layer", code_range=0x200, macro_regex=r"^#define\s+(?P<name>[_A-Z0-9]+)\s+CHIP_DEVICE_ERROR[(](?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- "src/lib/asn1/ASN1Error.h": ErrorDescriptor(section="ASN1 Layer", code_range=0x300, macro_regex=r"^#define\s+(?P<name>[_A-Z0-9]+)\s+CHIP_ASN1_ERROR[(](?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- "src/ble/BleError.h": ErrorDescriptor(section="BLE Layer", code_range=0x400, macro_regex=r"^#define\s+(?P<name>[_A-Z0-9]+)\s+CHIP_BLE_ERROR[(](?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- "src/protocols/interaction_model/StatusCodeList.h": ErrorDescriptor(section="IM Global errors", code_range=0x500, macro_regex=r"^CHIP_IM_STATUS_CODE[(][A-Za-z0-9_]+\s*,\s*(?P<name>[A-Z0-9_]+)\s*,\s*(?P<code>(0x[a-fA-F0-9]+)|\d+)[)]"),
- }
- print("# Matter SDK `CHIP_ERROR` enums values")
- print()
- print("This file was **AUTOMATICALLY** generated by `python scripts/error_table.py > docs/ERROR_CODES.md`.")
- print("DO NOT EDIT BY HAND!")
- print()
- print("## Table of contents")
- for descriptor in descriptors.values():
- markdown_title, anchor_name = get_section_title(descriptor.section)
- print(f"- [{markdown_title}: range `0x{descriptor.code_range:03X}..0x{descriptor.code_range | 0xFF:03X}`]({anchor_name})")
- print()
- for filename, descriptor in descriptors.items():
- dump_table(Path(filename), descriptor)
- if __name__ == "__main__":
- main()
|