Skip to content

Add espressif esp-idf-size to the build engine to show firmware metrics #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3347298
Update arduino.py
Jason2866 May 20, 2025
873aedd
add esp-idf-size skeleton
Jason2866 May 20, 2025
2cf285f
import esp-idf-size
Jason2866 May 20, 2025
3093ce5
Update main.py
Jason2866 May 20, 2025
247ed70
Update arduino.py
Jason2866 May 20, 2025
78a5cc7
Update arduino.py
Jason2866 May 20, 2025
f81b606
Update arduino.py
Jason2866 May 20, 2025
2b9ff52
Update main.py
Jason2866 May 20, 2025
1114348
Update arduino.py
Jason2866 May 20, 2025
416c354
Update main.py
Jason2866 May 20, 2025
a741147
Update main.py
Jason2866 May 20, 2025
0f22836
fix syntax
Jason2866 May 20, 2025
30288f1
Update main.py
Jason2866 May 20, 2025
1a67a1c
again syntax
Jason2866 May 20, 2025
00fd971
Update espidf.py
Jason2866 May 21, 2025
27f0563
try to avoid littering comsole
Jason2866 May 21, 2025
92b4bbf
call chain wrong
Jason2866 May 21, 2025
d7e03ba
next try to silence command echo
Jason2866 May 21, 2025
15acc41
try workaround to silence scons command output
Jason2866 May 21, 2025
cc8eab1
Update main.py
Jason2866 May 21, 2025
20f11e4
Suppress showing python command call
Jason2866 May 21, 2025
10a5dd5
Update main.py
Jason2866 May 21, 2025
96c43bf
again syntax error in building call
Jason2866 May 21, 2025
b20cb77
switch off pio target size calculation
Jason2866 May 21, 2025
9924ca2
test
Jason2866 May 21, 2025
1369364
try different to add action
Jason2866 May 21, 2025
4ab25d0
revert to previous way
Jason2866 May 21, 2025
7faf0bc
start esp-idf-size with flag `-DSHOW_METRICS`
Jason2866 May 21, 2025
33a8387
env "CPPDEFINES" is not always set
Jason2866 May 21, 2025
4902fbd
debug print
Jason2866 May 21, 2025
0b82362
debug print LINKFLAGS
Jason2866 May 21, 2025
3c8ca6f
Update main.py
Jason2866 May 21, 2025
896951a
Update arduino.py
Jason2866 May 21, 2025
f54fc29
remove debug print
Jason2866 May 21, 2025
c1350b9
map name is not always `firmware`
Jason2866 May 21, 2025
ee1663a
use Metrics with idf
Jason2866 May 21, 2025
bc76881
debug print idf LinkerFlags
Jason2866 May 21, 2025
9df3c79
move to idf build script
Jason2866 May 21, 2025
89e6226
espidf needs map linker command in cmake
Jason2866 May 21, 2025
83db57f
debug print cmake args
Jason2866 May 21, 2025
5648121
different path for IDF for MAP file
Jason2866 May 21, 2025
07d1cfb
remove debug code
Jason2866 May 21, 2025
9eb208b
remove old idf map file
Jason2866 May 21, 2025
fb57a69
store espidf map file in build dir
Jason2866 May 21, 2025
3b0ecda
fix var expansion
Jason2866 May 21, 2025
3adf9a7
add info about failed install of esp-idf-size
Jason2866 May 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion builder/frameworks/arduino.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def _get_installed_pip_packages():
"wheel": ">=0.35.1",
"rich-click": ">=1.8.6",
"PyYAML": ">=6.0.2",
"intelhex": ">=2.3.0"
"intelhex": ">=2.3.0",
"esp-idf-size": ">=1.6.1"
}

installed_packages = _get_installed_pip_packages()
Expand Down
44 changes: 27 additions & 17 deletions builder/frameworks/espidf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
env = DefaultEnvironment()
env.SConscript("_embed_files.py", exports="env")

# remove maybe existing old map file in project root
map_file = os.path.join(env.subst("$PROJECT_DIR"), env.subst("$PROGNAME") + ".map")
if os.path.exists(map_file):
os.remove(map_file)

def install_standard_python_deps():
def _get_installed_standard_pip_packages():
result = {}
Expand Down Expand Up @@ -83,7 +88,8 @@ def _get_installed_standard_pip_packages():
deps = {
"wheel": ">=0.35.1",
"rich-click": ">=1.8.6",
"PyYAML": ">=6.0.2"
"PyYAML": ">=6.0.2",
"esp-idf-size": ">=1.6.1"
}

installed_packages = _get_installed_standard_pip_packages()
Expand Down Expand Up @@ -147,13 +153,6 @@ def _get_installed_standard_pip_packages():
assert os.path.isdir(FRAMEWORK_DIR)
assert os.path.isdir(TOOLCHAIN_DIR)

if (
["espidf"] == env.get("PIOFRAMEWORK")
and semantic_version.Version.coerce(__version__)
<= semantic_version.Version("6.1.10")
and "__debug" in COMMAND_LINE_TARGETS
):
print("Warning! Debugging an IDF project requires PlatformIO Core >= 6.1.11!")

if "arduino" in env.subst("$PIOFRAMEWORK"):
ARDUINO_FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
Expand Down Expand Up @@ -401,7 +400,6 @@ def get_project_lib_includes(env):

return paths


def is_cmake_reconfigure_required(cmake_api_reply_dir):
cmake_cache_file = os.path.join(BUILD_DIR, "CMakeCache.txt")
cmake_txt_files = [
Expand Down Expand Up @@ -1791,18 +1789,30 @@ def get_python_exe():
LIBSOURCE_DIRS=[os.path.join(ARDUINO_FRAMEWORK_DIR, "libraries")]
)

extra_cmake_args = [
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
]

if "CPPDEFINES" in env:
flatten_cppdefines = env.Flatten(env['CPPDEFINES'])
if "SHOW_METRICS" in flatten_cppdefines:
# This will add the linker flag for the map file
extra_cmake_args.append(
f'-DCMAKE_EXE_LINKER_FLAGS=-Wl,-Map={os.path.join(BUILD_DIR, env.subst("$PROGNAME") + ".map")}'
)

# Add any extra args from board config
extra_cmake_args += click.parser.split_arg_string(board.get("build.cmake_extra_args", ""))

print("Reading CMake configuration...")
project_codemodel = get_cmake_code_model(
PROJECT_DIR,
BUILD_DIR,
[
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
]
+ click.parser.split_arg_string(board.get("build.cmake_extra_args", "")),
extra_cmake_args
)

# At this point the sdkconfig file should be generated by the underlying build system
Expand Down
42 changes: 27 additions & 15 deletions builder/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
DefaultEnvironment)

from platformio.util import get_serial_ports
from platformio.project.helpers import get_project_dir

import os

env = DefaultEnvironment()
platform = env.PioPlatform()
Expand Down Expand Up @@ -68,12 +71,10 @@ def _normalize_frequency(frequency):
frequency = str(frequency).replace("L", "")
return str(int(int(frequency) / 1000000)) + "m"


def _get_board_f_flash(env):
frequency = env.subst("$BOARD_F_FLASH")
return _normalize_frequency(frequency)


def _get_board_f_image(env):
board_config = env.BoardConfig()
if "build.f_image" in board_config:
Expand All @@ -88,7 +89,6 @@ def _get_board_f_boot(env):

return _get_board_f_flash(env)


def _get_board_flash_mode(env):
if _get_board_memory_type(env) in (
"opi_opi",
Expand All @@ -101,15 +101,13 @@ def _get_board_flash_mode(env):
return "dio"
return mode


def _get_board_boot_mode(env):
memory_type = env.BoardConfig().get("build.arduino.memory_type", "")
build_boot = env.BoardConfig().get("build.boot", "$BOARD_FLASH_MODE")
if memory_type in ("opi_opi", "opi_qspi"):
build_boot = "opi"
return build_boot


def _parse_size(value):
if isinstance(value, int):
return value
Expand All @@ -122,7 +120,6 @@ def _parse_size(value):
return int(value[:-1]) * base
return value


def _parse_partitions(env):
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
if not isfile(partitions_csv):
Expand Down Expand Up @@ -163,7 +160,6 @@ def _parse_partitions(env):
env["INTEGRATION_EXTRA_DATA"].update({"application_offset": str(hex(app_offset))})
return result


def _update_max_upload_size(env):
if not env.get("PARTITIONS_TABLE_CSV"):
return
Expand Down Expand Up @@ -192,17 +188,13 @@ def _update_max_upload_size(env):
board.update("upload.maximum_size", _parse_size(p["size"]))
break



def _to_unix_slashes(path):
return path.replace("\\", "/")


#
# Filesystem helpers
#


def fetch_fs_size(env):
fs = None
for p in _parse_partitions(env):
Expand All @@ -226,17 +218,15 @@ def fetch_fs_size(env):
env["FS_START"] += 4096
env["FS_SIZE"] -= 4096


def __fetch_fs_size(target, source, env):
fetch_fs_size(env)
return (target, source)


board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
toolchain_arch = "xtensa-%s" % mcu
filesystem = board.get("build.filesystem", "spiffs")
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"):
if mcu in ("esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"):
toolchain_arch = "riscv32-esp"

if "INTEGRATION_EXTRA_DATA" not in env:
Expand All @@ -257,7 +247,7 @@ def __fetch_fs_size(target, source, env):
GDB=join(
platform.get_package_dir(
"tool-riscv32-esp-elf-gdb"
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4")
if mcu in ("esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4")
else "tool-xtensa-esp-elf-gdb"
)
or "",
Expand Down Expand Up @@ -367,6 +357,24 @@ def check_lib_archive_exists():
if not env.get("PIOFRAMEWORK"):
env.SConscript("frameworks/_bare.py", exports="env")


def firmware_metrics(target, source, env):
map_file = os.path.join(env.subst("$BUILD_DIR"), env.subst("$PROGNAME") + ".map")
if not os.path.isfile(map_file):
# map file can be in project dir
map_file = os.path.join(get_project_dir(), env.subst("$PROGNAME") + ".map")

if os.path.isfile(map_file):
try:
import subprocess
# Show output of esp_idf_size, but suppresses the command echo
subprocess.run([
env.subst("$PYTHONEXE"), "-m", "esp_idf_size", "--ng", map_file
], check=False)
except Exception:
print("Warning: Failed to run firmware metrics. Is esp-idf-size installed?")
pass

#
# Target: Build executable and linkable firmware or FS image
#
Expand All @@ -381,6 +389,9 @@ def check_lib_archive_exists():
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
else:
target_elf = env.BuildProgram()
silent_action = env.Action(firmware_metrics)
silent_action.strfunction = lambda target, source, env: '' # hack to silence scons command output
env.AddPostAction(target_elf, silent_action)
if set(["buildfs", "uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
target_firm = env.DataToBin(
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
Expand Down Expand Up @@ -565,6 +576,7 @@ def check_lib_archive_exists():
else:
sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol)


env.AddPlatformTarget("upload", target_firm, upload_actions, "Upload")
env.AddPlatformTarget("uploadfs", target_firm, upload_actions, "Upload Filesystem Image")
env.AddPlatformTarget(
Expand Down
3 changes: 3 additions & 0 deletions examples/espidf-blink/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ build_flags =
-D CONFIG_BLINK_GPIO=2
-D CONFIG_BLINK_LED_GPIO=2
-D CONFIG_BLINK_PERIOD=1000
-D SHOW_METRICS

[env:esp32-c2-devkitm-1]
platform = espressif32
Expand All @@ -27,6 +28,7 @@ build_flags =
-D CONFIG_BLINK_GPIO=8
-D CONFIG_BLINK_LED_GPIO=8
-D CONFIG_BLINK_PERIOD=1000
-D SHOW_METRICS

[env:esp32-c6-devkitc-1]
platform = espressif32
Expand All @@ -37,3 +39,4 @@ build_flags =
-D CONFIG_BLINK_GPIO=2
-D CONFIG_BLINK_LED_GPIO=2
-D CONFIG_BLINK_PERIOD=1000
-D SHOW_METRICS
3 changes: 3 additions & 0 deletions examples/espidf-coap-server/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ board_build.embed_txtfiles =

[env:esp-wrover-kit]
board = esp-wrover-kit
build_flags =
-D SHOW_METRICS