feat(cmds): Polish the public API, unify arg names, pack some args

BREAKING CHANGE
This commit is contained in:
Radim Karniš
2025-02-26 11:05:07 +01:00
parent 063d9d5fba
commit 37a13a94c8
6 changed files with 107 additions and 70 deletions

View File

@@ -303,7 +303,7 @@ The output of the command will be in ``raw`` format and gaps between individual
**RAW options:** **RAW options:**
* The ``--fill-flash-size SIZE`` option will pad the merged binary with `0xFF` bytes to the full flash specified size, for example ``--fill-flash-size 4MB`` will create a 4MB binary file. * The ``--pad-to-size SIZE`` option will pad the merged binary with `0xFF` bytes to the full flash specified size, for example ``--pad-to-size 4MB`` will create a 4MB binary file.
* The ``--target-offset 0xNNN`` option will create a merged binary that should be flashed at the specified offset, instead of at offset 0x0. * The ``--target-offset 0xNNN`` option will create a merged binary that should be flashed at the specified offset, instead of at offset 0x0.

View File

@@ -153,3 +153,21 @@ The esptool ``v5`` has switched to using `Click <https://click.palletsprojects.c
1. Remove the old shell completion code from your scripts and shell configuration files like ``.bashrc``, ``.zshrc``, ``.config/fish/config.fish``, etc. 1. Remove the old shell completion code from your scripts and shell configuration files like ``.bashrc``, ``.zshrc``, ``.config/fish/config.fish``, etc.
2. Follow the new shell completion setup instructions in the :ref:`shell-completion` section of the :ref:`installation <installation>` guide. 2. Follow the new shell completion setup instructions in the :ref:`shell-completion` section of the :ref:`installation <installation>` guide.
``merge_bin`` ``--fill-flash-size`` Argument
********************************************
The ``--fill-flash-size`` option of the :ref:`merge_bin <merge-bin>` command has been renamed to ``--pad-to-size``. This change provides a more intuitive and descriptive name for the argument and is consistent with the naming scheme in other esptool image manipulation commands.
**Migration Steps:**
1. Rename the ``--fill-flash-size`` to ``--pad-to-size`` in any existing ``merge_bin`` commands in scripts/CI pipelines.
``write_flash`` ``--ignore-flash-encryption-efuse-setting`` Argument
********************************************************************
The ``--ignore-flash-encryption-efuse-setting`` option of the :ref:`write_flash <write-flash>` command has been renamed to ``--ignore-flash-enc-efuse``. This change shortens the argument name to improve readability and consistency with other esptool options.
**Migration Steps:**
1. Rename the ``--ignore-flash-encryption-efuse-setting`` to ``--ignore-flash-enc-efuse`` in any existing ``write_flash`` commands in scripts/CI pipelines.

View File

@@ -123,7 +123,7 @@ click.rich_click.OPTION_GROUPS = {
"name": "RAW options", "name": "RAW options",
"options": [ "options": [
"--target-offset", "--target-offset",
"--fill-flash-size", "--pad-to-size",
], ],
}, },
], ],
@@ -570,9 +570,9 @@ def write_mem_cli(ctx, address, value, mask):
"filename, separated by space.", "filename, separated by space.",
) )
@click.option( @click.option(
"--ignore-flash-encryption-efuse-setting", "--ignore-flash-enc-efuse",
is_flag=True, is_flag=True,
help="Ignore flash encryption efuse settings", help="Ignore flash encryption eFuse settings",
) )
@click.option( @click.option(
"--force", "--force",
@@ -926,12 +926,12 @@ def read_flash_sfdp_cli(ctx, address, bytes, **kwargs):
help="Target offset where the output file will be flashed", help="Target offset where the output file will be flashed",
) )
@click.option( # RAW only @click.option( # RAW only
"--fill-flash-size", "--pad-to-size",
type=click.Choice( type=click.Choice(
["256KB", "512KB", "1MB", "2MB", "4MB", "8MB", "16MB", "32MB", "64MB", "128MB"] ["256KB", "512KB", "1MB", "2MB", "4MB", "8MB", "16MB", "32MB", "64MB", "128MB"]
), ),
help="If set, the final binary file will be padded with FF bytes up to this flash " help="If set, the final binary file will be padded with 0xFF bytes up to this flash"
"size.", " size.",
) )
@add_spi_flash_options(allow_keep=True, auto_detect=False) @add_spi_flash_options(allow_keep=True, auto_detect=False)
@click.pass_context @click.pass_context

View File

@@ -102,7 +102,7 @@ def detect_chip(
connect_attempts: Number of connection attempts before failing. connect_attempts: Number of connection attempts before failing.
Returns: Returns:
An instance of the detected chip class, initialized and ready for use. An initialized instance of the detected chip class ready for use.
""" """
inst = None inst = None
detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled)
@@ -257,7 +257,7 @@ def dump_mem(
output: Path to output file for binary data. If None, returns the data. output: Path to output file for binary data. If None, returns the data.
Returns: Returns:
bytes | None: Memory dump as bytes if filename is None; Memory dump as bytes if filename is None;
otherwise, returns None after writing to file. otherwise, returns None after writing to file.
""" """
log.print( log.print(
@@ -429,14 +429,7 @@ def write_flash(
flash_freq: str = "keep", flash_freq: str = "keep",
flash_mode: str = "keep", flash_mode: str = "keep",
flash_size: str = "keep", flash_size: str = "keep",
erase_all: bool = False, **kwargs,
encrypt: bool = False,
encrypt_files: list[tuple[int, BinaryIO]] | None = None,
compress: bool = False,
no_compress: bool = False,
force: bool = False,
ignore_flash_encryption_efuse_setting: bool = False,
no_progress: bool = False,
) -> None: ) -> None:
""" """
Write firmware or data to the SPI flash memory of an ESP device. Write firmware or data to the SPI flash memory of an ESP device.
@@ -451,15 +444,29 @@ def write_flash(
(``"keep"`` to retain current). (``"keep"`` to retain current).
flash_size: Flash size to set in the bootloader image header flash_size: Flash size to set in the bootloader image header
(``"keep"`` to retain current). (``"keep"`` to retain current).
erase_all: Erase the entire flash before writing.
encrypt: Encrypt all files during flashing. Keyword Args:
encrypt_files: List of (address, file) tuples for files to encrypt individually. erase_all (bool): Erase the entire flash before writing.
compress: Compress data before flashing. encrypt (bool): Encrypt all files during flashing.
no_compress: Don't compress data before flashing. encrypt_files (list[tuple[int, BinaryIO]] | None): List of (address, file)
force: Ignore safety checks (e.g., secure boot, flash size). tuples for files to encrypt individually.
ignore_flash_encryption_efuse_setting: Ignore flash encryption efuse settings. compress (bool): Compress data before flashing.
no_progress: Disable progress updates. no_compress (bool): Don't compress data before flashing.
force (bool): Ignore safety checks (e.g., overwriting bootloader, flash size).
ignore_flash_enc_efuse (bool): Ignore flash encryption eFuse settings.
no_progress (bool): Disable progress updates.
""" """
# Set default values of optional arguments
erase_all: bool = kwargs.get("erase_all", False)
encrypt: bool = kwargs.get("encrypt", False)
encrypt_files: list[tuple[int, BinaryIO]] | None = kwargs.get("encrypt_files", None)
compress: bool = kwargs.get("compress", False)
no_compress: bool = kwargs.get("no_compress", False)
force: bool = kwargs.get("force", False)
ignore_flash_enc_efuse: bool = kwargs.get("ignore_flash_enc_efuse", False)
no_progress: bool = kwargs.get("no_progress", False)
# set compress based on default behaviour: # set compress based on default behaviour:
# -> if either "compress" or "no_compress" is set, honour that # -> if either "compress" or "no_compress" is set, honour that
# -> otherwise, set "compress" unless the stub flasher is disabled # -> otherwise, set "compress" unless the stub flasher is disabled
@@ -575,7 +582,7 @@ def write_flash(
do_write = False do_write = False
if not do_write and not ignore_flash_encryption_efuse_setting: if not do_write and not ignore_flash_enc_efuse:
raise FatalError( raise FatalError(
"Can't perform encrypted flash write, " "Can't perform encrypted flash write, "
"consult Flash Encryption documentation for more information" "consult Flash Encryption documentation for more information"
@@ -1231,7 +1238,7 @@ def read_flash_sfdp(esp: ESPLoader, address: int, bytes: int = 1) -> None:
Args: Args:
esp: Initiated esp object connected to a real device. esp: Initiated esp object connected to a real device.
address: Starting address in the SFDP region to read from. address: Starting address in the SFDP region to read from.
bytes: Number of bytes to read (1-4, default: 1). bytes: Number of bytes to read (1-4).
""" """
if not (1 <= bytes <= 4): if not (1 <= bytes <= 4):
raise FatalError("Invalid number of bytes to read from SFDP (1-4).") raise FatalError("Invalid number of bytes to read from SFDP (1-4).")
@@ -1270,7 +1277,7 @@ def read_flash(
no_progress: Disable printing progress. no_progress: Disable printing progress.
Returns: Returns:
bytes | None: The read flash data as bytes if output is None; otherwise, The read flash data as bytes if output is None; otherwise,
returns None after writing to file. returns None after writing to file.
""" """
_set_flash_parameters(esp, flash_size) _set_flash_parameters(esp, flash_size)
@@ -1557,7 +1564,7 @@ def run_stub(esp: ESPLoader) -> ESPLoader:
esp: Initiated esp object connected to a real device. esp: Initiated esp object connected to a real device.
Returns: Returns:
ESPLoader: The esp instance, either as a stub child class in a state The esp instance, either as a stub child class in a state
where the stub has been executed, or in its original state where the stub has been executed, or in its original state
if the stub loader is disabled or unsupported. if the stub loader is disabled or unsupported.
""" """
@@ -1938,10 +1945,7 @@ def merge_bin(
flash_mode: str = "keep", flash_mode: str = "keep",
flash_size: str = "keep", flash_size: str = "keep",
format: str = "raw", format: str = "raw",
target_offset: int = 0, **kwargs,
fill_flash_size: str | None = None,
chunk_size: int | None = None,
md5_disable: bool = False,
) -> None: ) -> None:
""" """
Merge multiple binary files into a single output file for flashing to an ESP device. Merge multiple binary files into a single output file for flashing to an ESP device.
@@ -1962,11 +1966,20 @@ def merge_bin(
flash_size: Flash size to set in the image header flash_size: Flash size to set in the image header
(``"keep"`` to retain current). (``"keep"`` to retain current).
format: Output format (``"raw"``, ``"uf2"``, or ``"hex"``). format: Output format (``"raw"``, ``"uf2"``, or ``"hex"``).
target_offset: Starting offset for the merged output.
fill_flash_size: If specified, pad the output he given flash size. Keyword Args:
chunk_size: Chunk size for UF2 format. target_offset (int): Starting offset for the merged output.
md5_disable: If True, disable MD5 checks in UF2 format. pad_to_size (str | None): If specified, pad the output to a specific flash size.
chunk_size (int | None): Chunk size for UF2 format.
md5_disable (bool): If True, disable MD5 checks in UF2 format.
""" """
# Set default values of optional arguments
target_offset: int = kwargs.get("target_offset", 0)
pad_to_size: str | None = kwargs.get("pad_to_size", None)
chunk_size: int | None = kwargs.get("chunk_size", None)
md5_disable: bool = kwargs.get("md5_disable", False)
try: try:
chip_class = CHIP_DEFS[chip] chip_class = CHIP_DEFS[chip]
except KeyError: except KeyError:
@@ -2023,8 +2036,8 @@ def merge_bin(
chip_class, addr, flash_freq, flash_mode, flash_size, image chip_class, addr, flash_freq, flash_mode, flash_size, image
) )
of.write(image) of.write(image)
if fill_flash_size: if pad_to_size:
pad_to(flash_size_bytes(fill_flash_size)) pad_to(flash_size_bytes(pad_to_size))
log.print( log.print(
f"Wrote {of.tell():#x} bytes to file {output}, " f"Wrote {of.tell():#x} bytes to file {output}, "
f"ready to flash to offset {target_offset:#x}" f"ready to flash to offset {target_offset:#x}"
@@ -2061,18 +2074,7 @@ def elf2image(
flash_freq: str | None = None, flash_freq: str | None = None,
flash_mode: str = "qio", flash_mode: str = "qio",
flash_size: str = "1MB", flash_size: str = "1MB",
version: int = 1, **kwargs,
min_rev: int = 0,
min_rev_full: int = 0,
max_rev_full: int = 65535,
secure_pad: bool = False,
secure_pad_v2: bool = False,
elf_sha256_offset: int | None = None,
append_digest: bool = True,
use_segments: bool = False,
flash_mmu_page_size: str | None = None,
pad_to_size: str | None = None,
ram_only_header: bool = False,
) -> None: ) -> None:
""" """
Convert an ELF file into a firmware image suitable for flashing onto an ESP device. Convert an ELF file into a firmware image suitable for flashing onto an ESP device.
@@ -2084,19 +2086,36 @@ def elf2image(
flash_freq: Flash frequency to set in the image header. flash_freq: Flash frequency to set in the image header.
flash_mode: Flash mode to set in the image header. flash_mode: Flash mode to set in the image header.
flash_size: Flash size to set in the image header. flash_size: Flash size to set in the image header.
version: ESP8266 only, firmware image version.
min_rev: Minimum chip revision required. Keyword Args:
min_rev_full: Minimum full revision required. version (int): ESP8266-only, firmware image version.
max_rev_full: Maximum full revision allowed. min_rev (int): Minimum chip revision required in legacy format.
secure_pad: Enable secure padding, ESP32-only. min_rev_full (int): Minimum chip revision required in extended format.
secure_pad_v2: Enable version 2 secure padding. max_rev_full (int): Maximum chip revision allowed in extended format.
elf_sha256_offset: Offset for storing the ELF file's SHA-256 hash. secure_pad (bool): ESP32-only, enable secure padding.
append_digest: Whether to append a digest to the firmware image. secure_pad_v2 (bool): Enable version 2 secure padding.
use_segments: Use ELF segments instead of sections. elf_sha256_offset (int): Offset for storing the ELF file's SHA-256 hash.
flash_mmu_page_size: MMU page size for flash mapping. append_digest (bool): Whether to append a digest to the firmware image.
pad_to_size: Pad the final image to a specific flash size. use_segments (bool): Use ELF segments instead of sections.
ram_only_header: Image will only contain RAM segments and no SHA-256 digest. flash_mmu_page_size (str): MMU page size for flash mapping.
pad_to_size (str): Pad the final image to a specific flash size.
ram_only_header (bool): Include only RAM segments and no SHA-256 hash.
""" """
# Set default values of optional arguments
version: int = kwargs.get("version", 1)
min_rev: int = kwargs.get("min_rev", 0)
min_rev_full: int = kwargs.get("min_rev_full", 0)
max_rev_full: int = kwargs.get("max_rev_full", 65535)
secure_pad: bool = kwargs.get("secure_pad", False)
secure_pad_v2: bool = kwargs.get("secure_pad_v2", False)
elf_sha256_offset: int | None = kwargs.get("elf_sha256_offset", None)
append_digest: bool = kwargs.get("append_digest", True)
use_segments: bool = kwargs.get("use_segments", False)
flash_mmu_page_size: str | None = kwargs.get("flash_mmu_page_size", None)
pad_to_size: str | None = kwargs.get("pad_to_size", None)
ram_only_header: bool = kwargs.get("ram_only_header", False)
e = ELFFile(input) e = ELFFile(input)
if chip == "auto": # Default to ESP8266 for backwards compatibility if chip == "auto": # Default to ESP8266 for backwards compatibility
chip = "esp8266" chip = "esp8266"

View File

@@ -369,7 +369,7 @@ class TestFlashEncryption(EsptoolTestCase):
pytest.skip("Valid encryption key already programmed, aborting the test") pytest.skip("Valid encryption key already programmed, aborting the test")
self.run_esptool( self.run_esptool(
"write_flash --encrypt --ignore-flash-encryption-efuse-setting " "write_flash --encrypt --ignore-flash-enc-efuse "
"0x10000 images/ram_helloworld/helloworld-esp32.bin" "0x10000 images/ram_helloworld/helloworld-esp32.bin"
) )
self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin") self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin")
@@ -408,7 +408,7 @@ class TestFlashEncryption(EsptoolTestCase):
pytest.skip("Valid encryption key already programmed, aborting the test") pytest.skip("Valid encryption key already programmed, aborting the test")
self.run_esptool( self.run_esptool(
"write_flash --encrypt --ignore-flash-encryption-efuse-setting " "write_flash --encrypt --ignore-flash-enc-efuse "
"0x10000 images/ram_helloworld/helloworld-esp32_edit.bin" "0x10000 images/ram_helloworld/helloworld-esp32_edit.bin"
) )
self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin") self.run_esptool("read_flash 0x10000 192 images/read_encrypted_flash.bin")

View File

@@ -169,9 +169,9 @@ class TestMergeBin:
assert helloworld == merged[0xF000 : 0xF000 + len(helloworld)] assert helloworld == merged[0xF000 : 0xF000 + len(helloworld)]
self.assertAllFF(merged[0x1000 + len(bootloader) : 0xF000]) self.assertAllFF(merged[0x1000 + len(bootloader) : 0xF000])
def test_fill_flash_size(self): def test_pad_to_size(self):
merged = self.run_merge_bin( merged = self.run_merge_bin(
"esp32c3", [(0x0, "bootloader_esp32c3.bin")], ["--fill-flash-size", "4MB"] "esp32c3", [(0x0, "bootloader_esp32c3.bin")], ["--pad-to-size", "4MB"]
) )
bootloader = read_image("bootloader_esp32c3.bin") bootloader = read_image("bootloader_esp32c3.bin")
@@ -179,14 +179,14 @@ class TestMergeBin:
assert bootloader == merged[: len(bootloader)] assert bootloader == merged[: len(bootloader)]
self.assertAllFF(merged[len(bootloader) :]) self.assertAllFF(merged[len(bootloader) :])
def test_fill_flash_size_w_target_offset(self): def test_pad_to_size_w_target_offset(self):
merged = self.run_merge_bin( merged = self.run_merge_bin(
"esp32", "esp32",
[ [
(0x1000, "bootloader_esp32.bin"), (0x1000, "bootloader_esp32.bin"),
(0x10000, "ram_helloworld/helloworld-esp32.bin"), (0x10000, "ram_helloworld/helloworld-esp32.bin"),
], ],
["--target-offset", "0x1000", "--fill-flash-size", "2MB"], ["--target-offset", "0x1000", "--pad-to-size", "2MB"],
) )
# full length is without target-offset arg # full length is without target-offset arg
@@ -215,7 +215,7 @@ class TestMergeBin:
merged = self.run_merge_bin( merged = self.run_merge_bin(
"esp32", "esp32",
[(0x1000, f.name), (0x10000, "ram_helloworld/helloworld-esp32.bin")], [(0x1000, f.name), (0x10000, "ram_helloworld/helloworld-esp32.bin")],
["--target-offset", "0x1000", "--fill-flash-size", "2MB"], ["--target-offset", "0x1000", "--pad-to-size", "2MB"],
) )
finally: finally:
os.unlink(f.name) os.unlink(f.name)