1#!/usr/bin/env python3 2 3# Copyright (c) 2025 Arduino SA 4# SPDX-License-Identifier: Apache-2.0 5 6import argparse 7import os 8import pickle 9 10from tabulate import tabulate 11 12KIND_TO_COL = { 13 "unset": "not set", 14 "default": "default", 15 "assign": "assigned", 16 "select": "selected", 17 "imply": "implied", 18} 19 20 21def source_link(refpath, fn, ln): 22 if refpath: 23 fn = os.path.relpath(fn, refpath) 24 25 link_fn = fn 26 disp_fn = os.path.normpath(fn).replace("../", "").replace("_", "\\_") 27 28 return f"[{disp_fn}:{ln}](<{link_fn}#L{ln}>)" 29 30 31def write_markdown(trace_data, output): 32 sections = {"user": [], "hidden": [], "unset": []} 33 34 if os.name == "nt": 35 # relative paths on Windows can't span drives, so don't use them 36 # generated links will be absolute 37 refpath = None 38 else: 39 refpath = os.path.dirname(os.path.abspath(output)) 40 41 for sym in trace_data: 42 sym_name, sym_vis, sym_type, sym_value, sym_src, sym_loc = sym 43 if sym_vis == "n": 44 section = "hidden" 45 elif sym_src == "unset": 46 section = "unset" 47 else: 48 section = "user" 49 50 if section == "user": 51 sym_name = f"`{sym_name}`" 52 elif section == "hidden": 53 sym_name = f"`{sym_name}` (h)" 54 55 if sym_type == "string" and sym_value is not None: 56 sym_value = f'"{sym_value}"'.replace("_", "\\_") 57 58 if isinstance(sym_loc, tuple): 59 sym_loc = source_link(refpath, *sym_loc) 60 elif isinstance(sym_loc, list): 61 sym_loc = " || <br> ".join(f"`{loc}`" for loc in sym_loc) 62 sym_loc = sym_loc.replace("|", "\\|") 63 elif sym_loc is None and sym_src == "default": 64 sym_loc = "_(implicit)_" 65 66 sym_src = KIND_TO_COL[sym_src] 67 68 sections[section].append((sym_type, sym_name, sym_value, sym_src, sym_loc)) 69 70 lines = [] 71 add = lines.append 72 73 headers = ["Type", "Name", "Value", "Source", "Location"] 74 colaligns = ["right", "left", "right", "center", "left"] 75 76 add("\n## Visible symbols\n\n") 77 add( 78 tabulate( 79 sorted(sections["user"], key=lambda x: x[1]), 80 headers=headers, 81 tablefmt='pipe', 82 colalign=colaligns, 83 ) 84 ) 85 86 add("\n\n## Invisible symbols\n\n") 87 add( 88 tabulate( 89 sorted(sections["hidden"], key=lambda x: x[1]), 90 headers=headers, 91 tablefmt='pipe', 92 colalign=colaligns, 93 ) 94 ) 95 96 add("\n\n## Unset symbols\n\n") 97 for sym_name in sorted(x[1] for x in sections["unset"]): 98 add(f" # {sym_name} is not set\n") 99 100 with open(output, "w") as f: 101 f.writelines(lines) 102 103 104def main(): 105 parser = argparse.ArgumentParser(allow_abbrev=False) 106 parser.add_argument("dotconfig_file", help="Input merged .config file") 107 parser.add_argument("output_file", help="Output Markdown file") 108 parser.add_argument("kconfig_file", help="Top-level Kconfig file", nargs="?") 109 110 args = parser.parse_args() 111 112 with open(args.dotconfig_file + '-trace.pickle', 'rb') as f: 113 trace_data = pickle.load(f) 114 write_markdown(trace_data, args.output_file) 115 116 117if __name__ == '__main__': 118 main() 119