()
| 114 | |
| 115 | |
| 116 | def build_report() -> tuple[dict, dict]: |
| 117 | target_dir = env("TARGET_DIR") # noqa: F841 (kept to match size_report contract) |
| 118 | br2_output_dir = env("BR2_OUTPUT_DIR") |
| 119 | images_dir = env("IMAGES_DIR") |
| 120 | soc_model = env("OPENIPC_SOC_MODEL") |
| 121 | variant = env("OPENIPC_VARIANT") |
| 122 | br_ver = env("BR_VER", required=False, default="2024.02.10") |
| 123 | repo_root = env("PWD", required=False, default=os.getcwd()) |
| 124 | |
| 125 | srctree = setup_kconfig_env(br2_output_dir, br_ver, repo_root) |
| 126 | |
| 127 | import kconfiglib # imported late so missing-dep errors are scoped here |
| 128 | |
| 129 | config_in = os.path.join(srctree, "Config.in") |
| 130 | if not os.path.isfile(config_in): |
| 131 | sys.exit(f"kconfig_graph: Config.in not found at {config_in}") |
| 132 | |
| 133 | cwd = os.getcwd() |
| 134 | try: |
| 135 | os.chdir(srctree) |
| 136 | kc = kconfiglib.Kconfig("Config.in", warn_to_stderr=False) |
| 137 | dotconfig = os.path.join(br2_output_dir, ".config") |
| 138 | if not os.path.isfile(dotconfig): |
| 139 | sys.exit(f"kconfig_graph: .config not found at {dotconfig}") |
| 140 | kc.load_config(dotconfig) |
| 141 | finally: |
| 142 | os.chdir(cwd) |
| 143 | |
| 144 | type_to_str = kconfiglib.TYPE_TO_STR |
| 145 | expr_str = kconfiglib.expr_str |
| 146 | |
| 147 | def _walk_expr(expr) -> list[str]: |
| 148 | """Collect all symbol names referenced anywhere in a Kconfig expression.""" |
| 149 | out: list[str] = [] |
| 150 | |
| 151 | def rec(e): |
| 152 | if isinstance(e, kconfiglib.Symbol): |
| 153 | if e.name and e.name != "y" and e.name != "n": |
| 154 | out.append(e.name) |
| 155 | elif isinstance(e, tuple): |
| 156 | for child in e[1:]: |
| 157 | rec(child) |
| 158 | |
| 159 | rec(expr) |
| 160 | return sorted(set(out)) |
| 161 | |
| 162 | # Pre-build a reverse-select index: target_symbol -> [symbols that `select` |
| 163 | # it]. Walking `sym.rev_dep` directly collects symbols from the condition |
| 164 | # expression too (`depends on X` referenced in a `select target if X`), |
| 165 | # which is wrong for the "what hard-pins this on" UX — only direct |
| 166 | # selectors should appear in `selected_by`. |
| 167 | reverse_selects: dict[str, list[str]] = {} |
| 168 | for s in kc.unique_defined_syms: |
| 169 | if not s.name: |
| 170 | continue |
| 171 | for tgt, _cond in s.selects: |
| 172 | if not tgt.name: |
| 173 | continue |
no test coverage detected