Skip to content

Commit 915fa0b

Browse files
committed
Fix LibChecker icon map generation
Parse the current IconResMap intArrayOf format while keeping the legacy put format. Fail generation early when the parsed SDK icon count is suspiciously low.
1 parent 0d9258e commit 915fa0b

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
- Runtime generated files live under `packages/shared/src/generated/` and are ignored by git. Do not hand-edit them.
7979
- Scripts usually run `generated:generate` before checks/builds. If locale or generated-source behavior changes, run `npm run i18n:check` or `npm run check`.
8080
- LibChecker rules/icons are large generated assets. Use `npm run generated:refresh` or `npm run rules:update` only when intentionally refreshing those bundles.
81+
- LibChecker upstream rule metadata can change shape. Keep `generate_libchecker_bundle.py` compatible with current `IconResMap.kt` formats and fail fast on suspiciously low icon counts instead of letting Web UI perf budgets catch empty generated chunks.
8182
- Do not add custom metadata fields to Crowdin JSON locale files; the generator expects string trees. For brand names, file extensions, protocol names, JSON field names, SDK/ABI/signing terms, and other non-translatable Web UI constants, use runtime constants such as `NON_TRANSLATABLE_MESSAGES` in `packages/apk-webui/src/app/i18n.js`.
8283
- If using the Crowdin Translator MCP, it is configured as a remote MCP with `CROWDIN_API_TOKEN`; running Codex sessions may need a restart before a newly added MCP appears.
8384

packages/shared/scripts/generate_libchecker_bundle.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
DEFAULT_RULES_REF = "main"
2424
DEFAULT_RULE_DETAILS_REF = "v4"
25+
MIN_EXPECTED_SDK_ICON_COUNT = 50
2526

2627
RELEVANT_RULE_TYPES = {0, 1, 2, 3, 4, 9}
2728

@@ -65,6 +66,10 @@
6566

6667

6768
def main() -> int:
69+
if "--self-test" in sys.argv[1:]:
70+
run_self_test()
71+
return 0
72+
6873
rules_ref = parse_rules_ref(sys.argv[1:])
6974
rules_root = build_rules_bundle_root(rules_ref)
7075
rule_db_url = f"{rules_root}/assets/lcrules/rules.db"
@@ -106,6 +111,12 @@ def main() -> int:
106111
for icon_name, svg in list(icon_svgs.items()):
107112
if not svg:
108113
icon_svgs[icon_name] = placeholder_svg
114+
115+
if len(icon_svgs) < MIN_EXPECTED_SDK_ICON_COUNT:
116+
raise ValueError(
117+
f"parsed only {len(icon_svgs)} SDK icons from IconResMap; "
118+
"update parse_icon_res_map for the upstream format"
119+
)
109120
except Exception as exc:
110121
print(f"Failed to sync rules bundle from GitHub: {exc}", file=sys.stderr)
111122
return 1
@@ -188,14 +199,62 @@ def parse_icon_res_map(text: str) -> tuple[dict[int, str], set[int]]:
188199
index = int(match.group(1))
189200
icon_map[index] = match.group(2)
190201

191-
set_match = re.search(r"SINGLE_COLOR_ICON_SET = setOf\((.*?)\)", text, flags=re.S)
202+
array_match = re.search(r"iconResIds\s*=\s*intArrayOf\((.*?)\)", text, flags=re.S)
203+
if array_match:
204+
array_body = array_match.group(1)
205+
indexed_matches = list(re.finditer(r"/\*\s*(-?\d+)\s*\*/\s*R\.drawable\.(\w+)", array_body))
206+
if indexed_matches:
207+
for match in indexed_matches:
208+
icon_map[int(match.group(1))] = match.group(2)
209+
else:
210+
for index, icon_name in enumerate(re.findall(r"R\.drawable\.(\w+)", array_body)):
211+
icon_map[index] = icon_name
212+
213+
set_match = re.search(
214+
r"(?:SINGLE_COLOR_ICON_SET|singleColorIconIndexes)\s*=\s*setOf\((.*?)\)",
215+
text,
216+
flags=re.S,
217+
)
192218
if set_match:
193-
for number in re.findall(r"-?\d+", set_match.group(1)):
219+
set_body = set_match.group(1).replace("PLACEHOLDER_INDEX", "-1")
220+
for number in re.findall(r"-?\d+", set_body):
194221
single_color.add(int(number))
195222

196223
return icon_map, single_color
197224

198225

226+
def run_self_test() -> None:
227+
old_format = """
228+
object IconResMap {
229+
val SINGLE_COLOR_ICON_SET = setOf(-1, 2)
230+
init {
231+
put(-1, R.drawable.ic_sdk_placeholder)
232+
put(2, R.drawable.ic_lib_old)
233+
}
234+
}
235+
"""
236+
old_map, old_single_color = parse_icon_res_map(old_format)
237+
assert old_map == {-1: "ic_sdk_placeholder", 2: "ic_lib_old"}
238+
assert old_single_color == {-1, 2}
239+
240+
new_format = """
241+
object IconResMap {
242+
private const val PLACEHOLDER_INDEX = -1
243+
private val iconResIds = intArrayOf(
244+
/* 0 */ R.drawable.ic_lib_zero,
245+
/* 1 */ R.drawable.ic_lib_one,
246+
)
247+
private val singleColorIconIndexes = setOf(
248+
PLACEHOLDER_INDEX, 1,
249+
)
250+
}
251+
"""
252+
new_map, new_single_color = parse_icon_res_map(new_format)
253+
assert new_map == {0: "ic_lib_zero", 1: "ic_lib_one"}
254+
assert new_single_color == {-1, 1}
255+
print("generate_libchecker_bundle self-test passed")
256+
257+
199258
def load_rules(
200259
db_path: Path,
201260
icon_index_map: dict[int, str],

0 commit comments

Comments
 (0)