size gate: emit flash_bytes from ELF for targets without a packaged .bin (#10940)
* size gate: emit flash_bytes from ELF for targets without a packaged .bin nRF52 builds package hex/uf2/DFU zip but no raw .bin, so collect_sizes.py dropped budgeted envs like rak4631 entirely and the size-budget-gate failed closed. Emit ELF text+data as flash_bytes in the manifest and use it as the fallback flash measurement. * Factor shared size-tool invocation into run_size_tool helper
This commit is contained in:
co-authored by
GitHub
parent
8d20606203
commit
ba473bf529
+19
-8
@@ -5,10 +5,13 @@
|
||||
Output schema (consumed by bin/size_report.py):
|
||||
{"<env>": {"flash_bytes": <int>, "ram_bytes": <int>}}
|
||||
|
||||
flash_bytes is the size of the main firmware image (.bin). ram_bytes is the
|
||||
static RAM footprint (.data + .bss) emitted into the manifest by
|
||||
bin/platformio-custom.py; it is omitted for manifests that predate it, and
|
||||
size_report.py renders those as "n/a".
|
||||
flash_bytes is the size of the main firmware image (.bin); for targets whose
|
||||
packaged artifacts include no raw .bin (e.g. nRF52) it falls back to the
|
||||
flash_bytes value (ELF text + data) emitted into the manifest by
|
||||
bin/platformio-custom.py. ram_bytes is the static RAM footprint (.data + .bss)
|
||||
emitted into the manifest by bin/platformio-custom.py; either metric is
|
||||
omitted for manifests that predate it, and size_report.py renders those as
|
||||
"n/a".
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -42,11 +45,19 @@ def collect_sizes(manifest_dir):
|
||||
):
|
||||
bin_size = entry["bytes"]
|
||||
break
|
||||
# Fallback: flash footprint emitted into the manifest (ELF text + data),
|
||||
# for targets that package no raw .bin (e.g. nRF52 hex/uf2/DFU zip)
|
||||
if bin_size is None:
|
||||
flash_bytes = data.get("flash_bytes")
|
||||
if isinstance(flash_bytes, int) and not isinstance(flash_bytes, bool):
|
||||
bin_size = flash_bytes
|
||||
entry = {}
|
||||
if bin_size is not None:
|
||||
entry = {"flash_bytes": bin_size}
|
||||
ram_bytes = data.get("ram_bytes")
|
||||
if isinstance(ram_bytes, int) and not isinstance(ram_bytes, bool):
|
||||
entry["ram_bytes"] = ram_bytes
|
||||
entry["flash_bytes"] = bin_size
|
||||
ram_bytes = data.get("ram_bytes")
|
||||
if isinstance(ram_bytes, int) and not isinstance(ram_bytes, bool):
|
||||
entry["ram_bytes"] = ram_bytes
|
||||
if entry:
|
||||
sizes[board] = entry
|
||||
return sizes
|
||||
|
||||
|
||||
+47
-14
@@ -45,6 +45,27 @@ def infer_architecture(board_cfg):
|
||||
return "stm32"
|
||||
return None
|
||||
|
||||
def run_size_tool(env, flag, purpose):
|
||||
"""Run the toolchain size tool against the built ELF and return its output.
|
||||
|
||||
Shared plumbing for compute_ram_bytes / compute_flash_bytes. Returns None
|
||||
when the ELF is missing or the tool fails; the manifest then simply omits
|
||||
the metric and downstream size reports show "n/a".
|
||||
"""
|
||||
elf = env.File(env.subst("$BUILD_DIR/${PROGNAME}.elf"))
|
||||
if not elf.exists():
|
||||
return None
|
||||
size_tool = env.subst("$SIZETOOL") or "size"
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
[size_tool, flag, elf.get_abspath()],
|
||||
env=env["ENV"],
|
||||
universal_newlines=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"mtjson: skipping {purpose} ({size_tool} failed: {exc})")
|
||||
return None
|
||||
|
||||
def compute_ram_bytes(env):
|
||||
"""Static RAM usage (.data + .bss) of the ELF, via the toolchain size tool.
|
||||
|
||||
@@ -54,18 +75,8 @@ def compute_ram_bytes(env):
|
||||
Returns None when the value cannot be determined; the manifest then simply
|
||||
omits ram_bytes and downstream size reports show "n/a".
|
||||
"""
|
||||
elf = env.File(env.subst("$BUILD_DIR/${PROGNAME}.elf"))
|
||||
if not elf.exists():
|
||||
return None
|
||||
size_tool = env.subst("$SIZETOOL") or "size"
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[size_tool, "-A", elf.get_abspath()],
|
||||
env=env["ENV"],
|
||||
universal_newlines=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"mtjson: skipping ram_bytes ({size_tool} failed: {exc})")
|
||||
output = run_size_tool(env, "-A", "ram_bytes")
|
||||
if output is None:
|
||||
return None
|
||||
ram = 0
|
||||
found = False
|
||||
@@ -88,6 +99,24 @@ def compute_ram_bytes(env):
|
||||
continue
|
||||
return ram if found else None
|
||||
|
||||
def compute_flash_bytes(env):
|
||||
"""Flash footprint (text + data) of the ELF via the toolchain size tool.
|
||||
|
||||
Approximates the flashed app image for targets whose packaged artifacts do
|
||||
not include a raw .bin (e.g. nRF52: hex/uf2/DFU zip only); consumed by
|
||||
bin/collect_sizes.py as a fallback flash measurement for the size report
|
||||
and the bin/ram_budgets.json budget gate. Returns None when the value
|
||||
cannot be determined; the manifest then simply omits flash_bytes.
|
||||
"""
|
||||
output = run_size_tool(env, "-B", "flash_bytes")
|
||||
if output is None:
|
||||
return None
|
||||
for line in output.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3 and parts[0].isdigit() and parts[1].isdigit():
|
||||
return int(parts[0]) + int(parts[1])
|
||||
return None
|
||||
|
||||
def manifest_gather(source, target, env):
|
||||
global manifest_ran
|
||||
if manifest_ran:
|
||||
@@ -141,9 +170,9 @@ def manifest_gather(source, target, env):
|
||||
d["part_name"] = partition_map[p]
|
||||
out.append(d)
|
||||
print(d)
|
||||
manifest_write(out, env, compute_ram_bytes(env))
|
||||
manifest_write(out, env, compute_ram_bytes(env), compute_flash_bytes(env))
|
||||
|
||||
def manifest_write(files, env, ram_bytes=None):
|
||||
def manifest_write(files, env, ram_bytes=None, flash_bytes=None):
|
||||
# Defensive: also skip manifest writing if we cannot determine architecture
|
||||
def get_project_option(name):
|
||||
try:
|
||||
@@ -184,6 +213,10 @@ def manifest_write(files, env, ram_bytes=None):
|
||||
# the CI size report and the bin/ram_budgets.json budget gate.
|
||||
if ram_bytes is not None:
|
||||
manifest["ram_bytes"] = ram_bytes
|
||||
# Flash footprint (text + data); fallback flash measurement for targets
|
||||
# whose packaged artifacts include no raw .bin (e.g. nRF52).
|
||||
if flash_bytes is not None:
|
||||
manifest["flash_bytes"] = flash_bytes
|
||||
# Get partition table (generated in esp32_pre.py) if it exists
|
||||
if env.get("custom_mtjson_part"):
|
||||
# custom_mtjson_part is a JSON string, convert it back to a dict
|
||||
|
||||
Reference in New Issue
Block a user