components: Correct the Python coding style

This commit is contained in:
Roland Dobai 2018-12-04 13:06:46 +01:00
parent c69907a54b
commit e1e6c1ae0a
6 changed files with 326 additions and 300 deletions

13
.flake8
View file

@ -150,22 +150,15 @@ exclude =
components/unity/unity, components/unity/unity,
examples/build_system/cmake/import_lib/main/lib/tinyxml2 examples/build_system/cmake/import_lib/main/lib/tinyxml2
# autogenerated scripts # autogenerated scripts
examples/provisioning/custom_config/components/custom_provisioning/python/custom_config_pb2.py,
# temporary list (should be empty)
components/app_update/dump_otadata.py,
components/app_update/gen_empty_partition.py,
components/espcoredump/espcoredump.py,
components/espcoredump/test/test_espcoredump.py,
components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py,
components/partition_table/gen_esp32part.py,
components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py,
components/protocomm/python/constants_pb2.py, components/protocomm/python/constants_pb2.py,
components/protocomm/python/sec0_pb2.py, components/protocomm/python/sec0_pb2.py,
components/protocomm/python/sec1_pb2.py, components/protocomm/python/sec1_pb2.py,
components/protocomm/python/session_pb2.py, components/protocomm/python/session_pb2.py,
components/ulp/esp32ulp_mapgen.py,
components/wifi_provisioning/python/wifi_config_pb2.py, components/wifi_provisioning/python/wifi_config_pb2.py,
components/wifi_provisioning/python/wifi_constants_pb2.py, components/wifi_provisioning/python/wifi_constants_pb2.py,
examples/provisioning/custom_config/components/custom_provisioning/python/custom_config_pb2.py,
# temporary list (should be empty)
components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py,
tools/ci/apply_bot_filter.py, tools/ci/apply_bot_filter.py,
tools/cmake/convert_to_cmake.py, tools/cmake/convert_to_cmake.py,
tools/esp_app_trace/apptrace_proc.py, tools/esp_app_trace/apptrace_proc.py,

View file

@ -5,6 +5,7 @@
from __future__ import print_function from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from __future__ import division from __future__ import division
import sys
try: try:
from builtins import zip from builtins import zip
from builtins import str from builtins import str
@ -15,13 +16,11 @@ except ImportError:
print('Import has failed probably because of the missing "future" package. Please install all the packages for ' print('Import has failed probably because of the missing "future" package. Please install all the packages for '
'interpreter {} from the $IDF_PATH/requirements.txt file.'.format(sys.executable)) 'interpreter {} from the $IDF_PATH/requirements.txt file.'.format(sys.executable))
sys.exit(1) sys.exit(1)
import sys
import os import os
import argparse import argparse
import subprocess import subprocess
import tempfile import tempfile
import struct import struct
import array
import errno import errno
import base64 import base64
import binascii import binascii
@ -53,6 +52,7 @@ class ESPCoreDumpError(RuntimeError):
""" """
super(ESPCoreDumpError, self).__init__(message) super(ESPCoreDumpError, self).__init__(message)
class BinStruct(object): class BinStruct(object):
"""Binary structure representation """Binary structure representation
@ -359,8 +359,8 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
f.seek(offs) f.seek(offs)
return f.read(size) return f.read(size)
prog_sections = [ESPCoreDumpSection(lookup_string(n_offs), lma, read_data(offs, size), flags) for (n_offs, _type, flags, lma, size, offs) in prog_sections prog_sections = [ESPCoreDumpSection(lookup_string(n_offs), lma, read_data(offs, size), flags)
if lma != 0] for (n_offs, _type, flags, lma, size, offs) in prog_sections if lma != 0]
self.sections = prog_sections self.sections = prog_sections
def _read_program_segments(self, f, seg_table_offs, entsz, num): def _read_program_segments(self, f, seg_table_offs, entsz, num):
@ -387,8 +387,8 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
f.seek(offs) f.seek(offs)
return f.read(size) return f.read(size)
self.program_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags) for (type, offset, vaddr, filesz,flags) in prog_segments self.program_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags)
if vaddr != 0] for (type, offset, vaddr, filesz,flags) in prog_segments if vaddr != 0]
def add_program_segment(self, addr, data, type, flags): def add_program_segment(self, addr, data, type, flags):
"""Adds new program segment """Adds new program segment
@ -492,21 +492,21 @@ class ESPCoreDumpLoader(object):
REG_LE_IDX = 3 REG_LE_IDX = 3
REG_LC_IDX = 4 REG_LC_IDX = 4
REG_SAR_IDX = 5 REG_SAR_IDX = 5
REG_WS_IDX=6 # REG_WS_IDX = 6
REG_WB_IDX=7 # REG_WB_IDX = 7
REG_AR_START_IDX = 64 REG_AR_START_IDX = 64
REG_AR_NUM=64 # REG_AR_NUM = 64
# FIXME: acc to xtensa_elf_gregset_t number of regs must be 128, # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128,
# but gdb complanis when it less then 129 # but gdb complanis when it less then 129
REG_NUM = 129 REG_NUM = 129
XT_SOL_EXIT=0 # XT_SOL_EXIT = 0
XT_SOL_PC = 1 XT_SOL_PC = 1
XT_SOL_PS = 2 XT_SOL_PS = 2
XT_SOL_NEXT=3 # XT_SOL_NEXT = 3
XT_SOL_AR_START = 4 XT_SOL_AR_START = 4
XT_SOL_AR_NUM = 4 XT_SOL_AR_NUM = 4
XT_SOL_FRMSZ=8 # XT_SOL_FRMSZ = 8
XT_STK_EXIT = 0 XT_STK_EXIT = 0
XT_STK_PC = 1 XT_STK_PC = 1
@ -514,8 +514,8 @@ class ESPCoreDumpLoader(object):
XT_STK_AR_START = 3 XT_STK_AR_START = 3
XT_STK_AR_NUM = 16 XT_STK_AR_NUM = 16
XT_STK_SAR = 19 XT_STK_SAR = 19
XT_STK_EXCCAUSE=20 # XT_STK_EXCCAUSE = 20
XT_STK_EXCVADDR=21 # XT_STK_EXCVADDR = 21
XT_STK_LBEG = 22 XT_STK_LBEG = 22
XT_STK_LEND = 23 XT_STK_LEND = 23
XT_STK_LCOUNT = 24 XT_STK_LCOUNT = 24
@ -550,14 +550,14 @@ class ESPCoreDumpLoader(object):
regs[REG_PS_IDX] = stack[XT_SOL_PS] regs[REG_PS_IDX] = stack[XT_SOL_PS]
for i in range(XT_SOL_AR_NUM): for i in range(XT_SOL_AR_NUM):
regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i] regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i]
nxt = stack[XT_SOL_NEXT] # nxt = stack[XT_SOL_NEXT]
# TODO: remove magic hack with saved PC to get proper value # TODO: remove magic hack with saved PC to get proper value
regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000) regs[REG_PC_IDX] = ((regs[REG_PC_IDX] & 0x3FFFFFFF) | 0x40000000)
if regs[REG_PC_IDX] & 0x80000000: if regs[REG_PC_IDX] & 0x80000000:
regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000; regs[REG_PC_IDX] = (regs[REG_PC_IDX] & 0x3fffffff) | 0x40000000
if regs[REG_AR_START_IDX + 0] & 0x80000000: if regs[REG_AR_START_IDX + 0] & 0x80000000:
regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000; regs[REG_AR_START_IDX + 0] = (regs[REG_AR_START_IDX + 0] & 0x3fffffff) | 0x40000000
return regs return regs
def remove_tmp_file(self, fname): def remove_tmp_file(self, fname):
@ -610,7 +610,8 @@ class ESPCoreDumpLoader(object):
data = self.read_data(core_off, tcbsz_aligned) data = self.read_data(core_off, tcbsz_aligned)
try: try:
if tcbsz != tcbsz_aligned: if tcbsz != tcbsz_aligned:
core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned],
ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
else: else:
core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W) core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
except ESPCoreDumpError as e: except ESPCoreDumpError as e:
@ -876,6 +877,7 @@ class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
""" """
TAG = '~' TAG = '~'
def load_aux_elf(elf_path): def load_aux_elf(elf_path):
""" Loads auxilary ELF file and composes GDB command to read its symbols """ Loads auxilary ELF file and composes GDB command to read its symbols
""" """
@ -888,6 +890,7 @@ def load_aux_elf(elf_path):
sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr) sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr)
return (elf, sym_cmd) return (elf, sym_cmd)
def dbg_corefile(args): def dbg_corefile(args):
""" Command to load core dump from file or flash and run GDB debug session with it """ Command to load core dump from file or flash and run GDB debug session with it
""" """
@ -911,13 +914,13 @@ def dbg_corefile(args):
loader.cleanup() loader.cleanup()
return return
p = subprocess.Popen( p = subprocess.Popen(bufsize=0,
bufsize = 0,
args=[args.gdb, args=[args.gdb,
'--nw', # ignore .gdbinit '--nw', # ignore .gdbinit
'--core=%s' % core_fname, # core file, '--core=%s' % core_fname, # core file,
'-ex', rom_sym_cmd, '-ex', rom_sym_cmd,
args.prog], args.prog
],
stdin=None, stdout=None, stderr=None, stdin=None, stdout=None, stderr=None,
close_fds=CLOSE_FDS close_fds=CLOSE_FDS
) )
@ -934,6 +937,7 @@ def info_corefile(args):
""" Command to load core dump from file or flash and print it's data in user friendly form """ Command to load core dump from file or flash and print it's data in user friendly form
""" """
global CLOSE_FDS global CLOSE_FDS
def gdbmi_console_stream_handler(ln): def gdbmi_console_stream_handler(ln):
sys.stdout.write(ln) sys.stdout.write(ln)
sys.stdout.flush() sys.stdout.flush()
@ -961,12 +965,10 @@ def info_corefile(args):
for c in gdb_cmds: for c in gdb_cmds:
gdb_args += ['-ex', c] gdb_args += ['-ex', c]
gdb_args.append(args.prog) gdb_args.append(args.prog)
p = subprocess.Popen( p = subprocess.Popen(bufsize=0,
bufsize = 0,
args=gdb_args, args=gdb_args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
close_fds = CLOSE_FDS close_fds=CLOSE_FDS)
)
gdbmi_read2prompt(p.stdout, handlers) gdbmi_read2prompt(p.stdout, handlers)
return p return p
@ -1130,9 +1132,12 @@ def main():
parser_debug_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=2) parser_debug_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=2)
parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf') parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) 'raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c"', type=str) parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash '
'(type "make partition_table" to see).', type=int, default=0x110000)
parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
'Ignored with "-c"', type=str)
parser_debug_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf') parser_debug_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
@ -1142,9 +1147,12 @@ def main():
parser_info_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=0) parser_info_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=0)
parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb') parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str) parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf') parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
parser_info_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash (type "make partition_table" to see).', type=int, default=0x110000) 'raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. Does not work with "-c"', type=str) parser_info_coredump.add_argument('--off', '-o', help='Offset of coredump partition in flash (type '
'"make partition_table" to see).', type=int, default=0x110000)
parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
'Does not work with "-c"', type=str)
parser_info_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf') parser_info_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true') parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true')
parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str) parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)

View file

@ -18,11 +18,15 @@ import sys
import os import os
import unittest import unittest
try:
import espcoredump
except ImportError:
idf_path = os.getenv('IDF_PATH') idf_path = os.getenv('IDF_PATH')
if idf_path: if idf_path:
sys.path.insert(0, os.path.join(idf_path, 'components', 'espcoredump')) sys.path.insert(0, os.path.join(idf_path, 'components', 'espcoredump'))
import espcoredump import espcoredump
class TestESPCoreDumpFileLoader(unittest.TestCase): class TestESPCoreDumpFileLoader(unittest.TestCase):
def setUp(self): def setUp(self):
self.tmp_file = 'tmp' self.tmp_file = 'tmp'
@ -43,6 +47,7 @@ class TestESPCoreDumpFileLoader(unittest.TestCase):
def test_create_corefile(self): def test_create_corefile(self):
self.assertEqual(self.dloader.create_corefile(core_fname=self.tmp_file, off=0, rom_elf=None), self.tmp_file) self.assertEqual(self.dloader.create_corefile(core_fname=self.tmp_file, off=0, rom_elf=None), self.tmp_file)
if __name__ == '__main__': if __name__ == '__main__':
# The purpose of these tests is to increase the code coverage at places which are sensitive to issues related to # The purpose of these tests is to increase the code coverage at places which are sensitive to issues related to
# Python 2&3 compatibility. # Python 2&3 compatibility.

View file

@ -71,16 +71,19 @@ md5sum = True
secure = False secure = False
offset_part_table = 0 offset_part_table = 0
def status(msg): def status(msg):
""" Print status message to stderr """ """ Print status message to stderr """
if not quiet: if not quiet:
critical(msg) critical(msg)
def critical(msg): def critical(msg):
""" Print critical message to stderr """ """ Print critical message to stderr """
sys.stderr.write(msg) sys.stderr.write(msg)
sys.stderr.write('\n') sys.stderr.write('\n')
class PartitionTable(list): class PartitionTable(list):
def __init__(self): def __init__(self):
super(PartitionTable, self).__init__(self) super(PartitionTable, self).__init__(self)
@ -149,14 +152,14 @@ class PartitionTable(list):
ptype = TYPES[ptype] ptype = TYPES[ptype]
except KeyError: except KeyError:
try: try:
ptypes = int(ptype, 0) ptype = int(ptype, 0)
except TypeError: except TypeError:
pass pass
try: try:
subtype = SUBTYPES[int(ptype)][subtype] subtype = SUBTYPES[int(ptype)][subtype]
except KeyError: except KeyError:
try: try:
ptypes = int(ptype, 0) ptype = int(ptype, 0)
except TypeError: except TypeError:
pass pass
@ -209,7 +212,7 @@ class PartitionTable(list):
@classmethod @classmethod
def from_binary(cls, b): def from_binary(cls, b):
md5 = hashlib.md5(); md5 = hashlib.md5()
result = cls() result = cls()
for o in range(0,len(b),32): for o in range(0,len(b),32):
data = b[o:o + 32] data = b[o:o + 32]
@ -242,6 +245,7 @@ class PartitionTable(list):
rows += [x.to_csv(simple_formatting) for x in self] rows += [x.to_csv(simple_formatting) for x in self]
return "\n".join(rows) + "\n" return "\n".join(rows) + "\n"
class PartitionDefinition(object): class PartitionDefinition(object):
MAGIC_BYTES = b"\xAA\x50" MAGIC_BYTES = b"\xAA\x50"
@ -353,12 +357,14 @@ class PartitionDefinition(object):
raise ValidationError(self, "Size field is not set") raise ValidationError(self, "Size field is not set")
if self.name in TYPES and TYPES.get(self.name, "") != self.type: if self.name in TYPES and TYPES.get(self.name, "") != self.type:
critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's type (0x%x). Mistake in partition table?" % (self.name, self.type)) critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's "
"type (0x%x). Mistake in partition table?" % (self.name, self.type))
all_subtype_names = [] all_subtype_names = []
for names in (t.keys() for t in SUBTYPES.values()): for names in (t.keys() for t in SUBTYPES.values()):
all_subtype_names += names all_subtype_names += names
if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, "") != self.subtype: if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, "") != self.subtype:
critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has non-matching type 0x%x and subtype 0x%x. Mistake in partition table?" % (self.name, self.type, self.subtype)) critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has "
"non-matching type 0x%x and subtype 0x%x. Mistake in partition table?" % (self.name, self.type, self.subtype))
STRUCT_FORMAT = b"<2sBBLL16sL" STRUCT_FORMAT = b"<2sBBLL16sL"
@ -404,7 +410,7 @@ class PartitionDefinition(object):
def lookup_keyword(t, keywords): def lookup_keyword(t, keywords):
for k,v in keywords.items(): for k,v in keywords.items():
if simple_formatting == False and t == v: if simple_formatting is False and t == v:
return k return k
return "%d" % t return "%d" % t
@ -437,6 +443,7 @@ def parse_int(v, keywords={}):
except KeyError: except KeyError:
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords))) raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
def main(): def main():
global quiet global quiet
global md5sum global md5sum
@ -448,7 +455,8 @@ def main():
nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB']) nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB'])
parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true') parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true')
parser.add_argument('--no-verify', help="Don't verify partition table fields", action='store_true') parser.add_argument('--no-verify', help="Don't verify partition table fields", action='store_true')
parser.add_argument('--verify', '-v', help="Verify partition table fields (deprecated, this behaviour is enabled by default and this flag does nothing.", action='store_true') parser.add_argument('--verify', '-v', help="Verify partition table fields (deprecated, this behaviour is "
"enabled by default and this flag does nothing.", action='store_true')
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true') parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000') parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
parser.add_argument('--secure', help="Require app partitions to be suitable for secure boot", action='store_true') parser.add_argument('--secure', help="Require app partitions to be suitable for secure boot", action='store_true')
@ -481,7 +489,8 @@ def main():
size = size_mb * 1024 * 1024 # flash memory uses honest megabytes! size = size_mb * 1024 * 1024 # flash memory uses honest megabytes!
table_size = table.flash_size() table_size = table.flash_size()
if size < table_size: if size < table_size:
raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which does not fit in configured flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu." % raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which does not fit in configured "
"flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu." %
(args.input.name, table_size / 1024.0 / 1024.0, table_size, size_mb)) (args.input.name, table_size / 1024.0 / 1024.0, table_size, size_mb))
# Make sure that the output directory is created # Make sure that the output directory is created

View file

@ -8,8 +8,13 @@ import subprocess
import tempfile import tempfile
import os import os
import io import io
import re
try:
import gen_esp32part
except ImportError:
sys.path.append("..") sys.path.append("..")
from gen_esp32part import * import gen_esp32part
SIMPLE_CSV = """ SIMPLE_CSV = """
# Name,Type,SubType,Offset,Size,Flags # Name,Type,SubType,Offset,Size,Flags
@ -53,6 +58,7 @@ def _strip_trailing_ffs(binary_table):
binary_table = binary_table[0:len(binary_table) - 32] binary_table = binary_table[0:len(binary_table) - 32]
return binary_table return binary_table
class Py23TestCase(unittest.TestCase): class Py23TestCase(unittest.TestCase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -64,10 +70,11 @@ class Py23TestCase(unittest.TestCase):
# This fix is used in order to avoid using the alias from the six library # This fix is used in order to avoid using the alias from the six library
self.assertRaisesRegex = self.assertRaisesRegexp self.assertRaisesRegex = self.assertRaisesRegexp
class CSVParserTests(Py23TestCase): class CSVParserTests(Py23TestCase):
def test_simple_partition(self): def test_simple_partition(self):
table = PartitionTable.from_csv(SIMPLE_CSV) table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
self.assertEqual(len(table), 1) self.assertEqual(len(table), 1)
self.assertEqual(table[0].name, "factory") self.assertEqual(table[0].name, "factory")
self.assertEqual(table[0].type, 0) self.assertEqual(table[0].type, 0)
@ -75,15 +82,13 @@ class CSVParserTests(Py23TestCase):
self.assertEqual(table[0].offset, 65536) self.assertEqual(table[0].offset, 65536)
self.assertEqual(table[0].size, 1048576) self.assertEqual(table[0].size, 1048576)
def test_require_type(self): def test_require_type(self):
csv = """ csv = """
# Name,Type, SubType,Offset,Size # Name,Type, SubType,Offset,Size
ihavenotype, ihavenotype,
""" """
with self.assertRaisesRegex(InputError, "type"): with self.assertRaisesRegex(gen_esp32part.InputError, "type"):
PartitionTable.from_csv(csv) gen_esp32part.PartitionTable.from_csv(csv)
def test_type_subtype_names(self): def test_type_subtype_names(self):
csv_magicnumbers = """ csv_magicnumbers = """
@ -106,9 +111,9 @@ myota_status, data, ota,, 0x100000
""" """
# make two equivalent partition tables, one using # make two equivalent partition tables, one using
# magic numbers and one using shortcuts. Ensure they match # magic numbers and one using shortcuts. Ensure they match
magic = PartitionTable.from_csv(csv_magicnumbers) magic = gen_esp32part.PartitionTable.from_csv(csv_magicnumbers)
magic.verify() magic.verify()
nomagic = PartitionTable.from_csv(csv_nomagicnumbers) nomagic = gen_esp32part.PartitionTable.from_csv(csv_nomagicnumbers)
nomagic.verify() nomagic.verify()
self.assertEqual(nomagic["myapp"].type, 0) self.assertEqual(nomagic["myapp"].type, 0)
@ -128,7 +133,7 @@ myota_status, data, ota,, 0x100000
# Name, Type, Subtype, Offset, Size # Name, Type, Subtype, Offset, Size
one_megabyte, app, factory, 64k, 1M one_megabyte, app, factory, 64k, 1M
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify() t.verify()
self.assertEqual(t[0].offset, 64 * 1024) self.assertEqual(t[0].offset, 64 * 1024)
self.assertEqual(t[0].size, 1 * 1024 * 1024) self.assertEqual(t[0].size, 1 * 1024 * 1024)
@ -141,7 +146,7 @@ second, data, 0x15,, 1M
minidata, data, 0x40,, 32K minidata, data, 0x40,, 32K
otherapp, app, factory,, 1M otherapp, app, factory,, 1M
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
# 'first' # 'first'
self.assertEqual(t[0].offset, 0x010000) # 64KB boundary as it's an app image self.assertEqual(t[0].offset, 0x010000) # 64KB boundary as it's an app image
self.assertEqual(t[0].size, 0x100000) # Size specified in CSV self.assertEqual(t[0].size, 0x100000) # Size specified in CSV
@ -159,7 +164,7 @@ otherapp, app, factory,, 1M
first, app, factory, 0x10000, -2M first, app, factory, 0x10000, -2M
second, data, 0x15, , 1M second, data, 0x15, , 1M
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify() t.verify()
# 'first' # 'first'
self.assertEqual(t[0].offset, 0x10000) # in CSV self.assertEqual(t[0].offset, 0x10000) # in CSV
@ -172,8 +177,8 @@ second, data, 0x15, , 1M
first, app, factory, 0x100000, 2M first, app, factory, 0x100000, 2M
second, app, ota_0, 0x200000, 1M second, app, ota_0, 0x200000, 1M
""" """
with self.assertRaisesRegex(InputError, "overlap"): with self.assertRaisesRegex(gen_esp32part.InputError, "overlap"):
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify() t.verify()
def test_unique_name_fail(self): def test_unique_name_fail(self):
@ -181,16 +186,17 @@ second, app, ota_0, 0x200000, 1M
first, app, factory, 0x100000, 1M first, app, factory, 0x100000, 1M
first, app, ota_0, 0x200000, 1M first, app, ota_0, 0x200000, 1M
""" """
with self.assertRaisesRegex(InputError, "Partition names must be unique"): with self.assertRaisesRegex(gen_esp32part.InputError, "Partition names must be unique"):
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify() t.verify()
class BinaryOutputTests(Py23TestCase): class BinaryOutputTests(Py23TestCase):
def test_binary_entry(self): def test_binary_entry(self):
csv = """ csv = """
first, 0x30, 0xEE, 0x100400, 0x300000 first, 0x30, 0xEE, 0x100400, 0x300000
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary()) tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 64 + 32) self.assertEqual(len(tb), 64 + 32)
self.assertEqual(b'\xAA\x50', tb[0:2]) # magic self.assertEqual(b'\xAA\x50', tb[0:2]) # magic
@ -206,22 +212,21 @@ first, 0x30, 0xEE, 0x100400, 0x300000
first, 0x30, 0xEE, 0x100400, 0x300000 first, 0x30, 0xEE, 0x100400, 0x300000
second,0x31, 0xEF, , 0x100000 second,0x31, 0xEF, , 0x100000
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary()) tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 96 + 32) self.assertEqual(len(tb), 96 + 32)
self.assertEqual(b'\xAA\x50', tb[0:2]) self.assertEqual(b'\xAA\x50', tb[0:2])
self.assertEqual(b'\xAA\x50', tb[32:34]) self.assertEqual(b'\xAA\x50', tb[32:34])
def test_encrypted_flag(self): def test_encrypted_flag(self):
csv = """ csv = """
# Name, Type, Subtype, Offset, Size, Flags # Name, Type, Subtype, Offset, Size, Flags
first, app, factory,, 1M, encrypted first, app, factory,, 1M, encrypted
""" """
t = PartitionTable.from_csv(csv) t = gen_esp32part.PartitionTable.from_csv(csv)
self.assertTrue(t[0].encrypted) self.assertTrue(t[0].encrypted)
tb = _strip_trailing_ffs(t.to_binary()) tb = _strip_trailing_ffs(t.to_binary())
tr = PartitionTable.from_binary(tb) tr = gen_esp32part.PartitionTable.from_binary(tb)
self.assertTrue(tr[0].encrypted) self.assertTrue(tr[0].encrypted)
@ -237,11 +242,11 @@ class BinaryParserTests(Py23TestCase):
b"\xFF" * 32 b"\xFF" * 32
# verify that parsing 32 bytes as a table # verify that parsing 32 bytes as a table
# or as a single Definition are the same thing # or as a single Definition are the same thing
t = PartitionTable.from_binary(entry) t = gen_esp32part.PartitionTable.from_binary(entry)
self.assertEqual(len(t), 1) self.assertEqual(len(t), 1)
t[0].verify() t[0].verify()
e = PartitionDefinition.from_binary(entry[:32]) e = gen_esp32part.PartitionDefinition.from_binary(entry[:32])
self.assertEqual(t[0], e) self.assertEqual(t[0], e)
e.verify() e.verify()
@ -252,14 +257,14 @@ class BinaryParserTests(Py23TestCase):
self.assertEqual(e.name, "0123456789abc") self.assertEqual(e.name, "0123456789abc")
def test_multiple_entries(self): def test_multiple_entries(self):
t = PartitionTable.from_binary(LONGER_BINARY_TABLE) t = gen_esp32part.PartitionTable.from_binary(LONGER_BINARY_TABLE)
t.verify() t.verify()
self.assertEqual(3, len(t)) self.assertEqual(3, len(t))
self.assertEqual(t[0].type, APP_TYPE) self.assertEqual(t[0].type, gen_esp32part.APP_TYPE)
self.assertEqual(t[0].name, "factory") self.assertEqual(t[0].name, "factory")
self.assertEqual(t[1].type, DATA_TYPE) self.assertEqual(t[1].type, gen_esp32part.DATA_TYPE)
self.assertEqual(t[1].name, "data") self.assertEqual(t[1].name, "data")
self.assertEqual(t[2].type, 0x10) self.assertEqual(t[2].type, 0x10)
@ -274,16 +279,16 @@ class BinaryParserTests(Py23TestCase):
b"\x00\x00\x20\x00" + \ b"\x00\x00\x20\x00" + \
b"0123456789abc\0\0\0" + \ b"0123456789abc\0\0\0" + \
b"\x00\x00\x00\x00" b"\x00\x00\x00\x00"
with self.assertRaisesRegex(InputError, "Invalid magic bytes"): with self.assertRaisesRegex(gen_esp32part.InputError, "Invalid magic bytes"):
PartitionTable.from_binary(bad_magic) gen_esp32part.PartitionTable.from_binary(bad_magic)
def test_bad_length(self): def test_bad_length(self):
bad_length = b"OHAI" + \ bad_length = b"OHAI" + \
b"\x00\x00\x10\x00" + \ b"\x00\x00\x10\x00" + \
b"\x00\x00\x20\x00" + \ b"\x00\x00\x20\x00" + \
b"0123456789" b"0123456789"
with self.assertRaisesRegex(InputError, "32 bytes"): with self.assertRaisesRegex(gen_esp32part.InputError, "32 bytes"):
PartitionTable.from_binary(bad_length) gen_esp32part.PartitionTable.from_binary(bad_length)
class CSVOutputTests(Py23TestCase): class CSVOutputTests(Py23TestCase):
@ -292,7 +297,7 @@ class CSVOutputTests(Py23TestCase):
return list(csv.reader(source_str.split("\n"))) return list(csv.reader(source_str.split("\n")))
def test_output_simple_formatting(self): def test_output_simple_formatting(self):
table = PartitionTable.from_csv(SIMPLE_CSV) table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
as_csv = table.to_csv(True) as_csv = table.to_csv(True)
c = self._readcsv(as_csv) c = self._readcsv(as_csv)
# first two lines should start with comments # first two lines should start with comments
@ -306,11 +311,11 @@ class CSVOutputTests(Py23TestCase):
self.assertEqual(row[4], "0x100000") # also hex self.assertEqual(row[4], "0x100000") # also hex
# round trip back to a PartitionTable and check is identical # round trip back to a PartitionTable and check is identical
roundtrip = PartitionTable.from_csv(as_csv) roundtrip = gen_esp32part.PartitionTable.from_csv(as_csv)
self.assertEqual(roundtrip, table) self.assertEqual(roundtrip, table)
def test_output_smart_formatting(self): def test_output_smart_formatting(self):
table = PartitionTable.from_csv(SIMPLE_CSV) table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
as_csv = table.to_csv(False) as_csv = table.to_csv(False)
c = self._readcsv(as_csv) c = self._readcsv(as_csv)
# first two lines should start with comments # first two lines should start with comments
@ -324,9 +329,10 @@ class CSVOutputTests(Py23TestCase):
self.assertEqual(row[4], "1M") self.assertEqual(row[4], "1M")
# round trip back to a PartitionTable and check is identical # round trip back to a PartitionTable and check is identical
roundtrip = PartitionTable.from_csv(as_csv) roundtrip = gen_esp32part.PartitionTable.from_csv(as_csv)
self.assertEqual(roundtrip, table) self.assertEqual(roundtrip, table)
class CommandLineTests(Py23TestCase): class CommandLineTests(Py23TestCase):
def test_basic_cmdline(self): def test_basic_cmdline(self):
@ -344,7 +350,7 @@ class CommandLineTests(Py23TestCase):
# reopen the CSV and check the generated binary is identical # reopen the CSV and check the generated binary is identical
self.assertNotIn(b"WARNING", output) self.assertNotIn(b"WARNING", output)
with open(csvpath, 'r') as f: with open(csvpath, 'r') as f:
from_csv = PartitionTable.from_csv(f.read()) from_csv = gen_esp32part.PartitionTable.from_csv(f.read())
self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE) self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE)
# run gen_esp32part.py to conver the CSV to binary again # run gen_esp32part.py to conver the CSV to binary again
@ -372,30 +378,29 @@ class VerificationTests(Py23TestCase):
# Name,Type, SubType,Offset,Size # Name,Type, SubType,Offset,Size
app,app, factory, 32K, 1M app,app, factory, 32K, 1M
""" """
with self.assertRaisesRegex(ValidationError, with self.assertRaisesRegex(gen_esp32part.ValidationError, r"Offset.+not aligned"):
r"Offset.+not aligned"): t = gen_esp32part.PartitionTable.from_csv(csv)
t = PartitionTable.from_csv(csv)
t.verify() t.verify()
def test_warnings(self): def test_warnings(self):
try: try:
sys.stderr = io.StringIO() # capture stderr sys.stderr = io.StringIO() # capture stderr
csv_1 = "app, 1, 2, 32K, 1M\n" csv_1 = "app, 1, 2, 32K, 1M\n"
PartitionTable.from_csv(csv_1).verify() gen_esp32part.PartitionTable.from_csv(csv_1).verify()
self.assertIn("WARNING", sys.stderr.getvalue()) self.assertIn("WARNING", sys.stderr.getvalue())
self.assertIn("partition type", sys.stderr.getvalue()) self.assertIn("partition type", sys.stderr.getvalue())
sys.stderr = io.StringIO() sys.stderr = io.StringIO()
csv_2 = "ota_0, app, ota_1, , 1M\n" csv_2 = "ota_0, app, ota_1, , 1M\n"
PartitionTable.from_csv(csv_2).verify() gen_esp32part.PartitionTable.from_csv(csv_2).verify()
self.assertIn("WARNING", sys.stderr.getvalue()) self.assertIn("WARNING", sys.stderr.getvalue())
self.assertIn("partition subtype", sys.stderr.getvalue()) self.assertIn("partition subtype", sys.stderr.getvalue())
finally: finally:
sys.stderr = sys.__stderr__ sys.stderr = sys.__stderr__
class PartToolTests(Py23TestCase): class PartToolTests(Py23TestCase):
def _run_parttool(self, csvcontents, args, info): def _run_parttool(self, csvcontents, args, info):
@ -404,7 +409,8 @@ class PartToolTests(Py23TestCase):
f.write(csvcontents) f.write(csvcontents)
try: try:
output = subprocess.check_output([sys.executable, "../parttool.py"] + args.split(" ") output = subprocess.check_output([sys.executable, "../parttool.py"] + args.split(" ")
+ ["--partition-table-file", csvpath , "get_partition_info", "--info", info], stderr=subprocess.STDOUT) + ["--partition-table-file", csvpath, "get_partition_info", "--info", info],
stderr=subprocess.STDOUT)
self.assertNotIn(b"WARNING", output) self.assertNotIn(b"WARNING", output)
m = re.search(b"0x[0-9a-fA-F]+", output) m = re.search(b"0x[0-9a-fA-F]+", output)
return m.group(0) if m else "" return m.group(0) if m else ""
@ -418,7 +424,9 @@ otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000 phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M factory, app, factory, 0x10000, 1M
""" """
rpt = lambda args, info: self._run_parttool(csv, args, info)
def rpt(args, info):
return self._run_parttool(csv, args, info)
self.assertEqual( self.assertEqual(
rpt("--partition-type=data --partition-subtype=nvs -q", "offset"), b"0x9000") rpt("--partition-type=data --partition-subtype=nvs -q", "offset"), b"0x9000")
@ -437,7 +445,9 @@ phy_init, data, phy, 0xf000, 0x1000
ota_0, app, ota_0, 0x30000, 1M ota_0, app, ota_0, 0x30000, 1M
ota_1, app, ota_1, , 1M ota_1, app, ota_1, , 1M
""" """
rpt = lambda args, info: self._run_parttool(csv, args, info)
def rpt(args, info):
return self._run_parttool(csv, args, info)
self.assertEqual( self.assertEqual(
rpt("--partition-type=app --partition-subtype=ota_1 -q", "offset"), b"0x130000") rpt("--partition-type=app --partition-subtype=ota_1 -q", "offset"), b"0x130000")
@ -448,5 +458,6 @@ ota_1, app, ota_1, , 1M
self._run_parttool(csv_mod, "--partition-boot-default -q", "offset"), self._run_parttool(csv_mod, "--partition-boot-default -q", "offset"),
b"0x130000") # now default is ota_1 b"0x130000") # now default is ota_1
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -7,27 +7,28 @@
from optparse import OptionParser from optparse import OptionParser
BASE_ADDR = 0x50000000; BASE_ADDR = 0x50000000
def gen_ld_h_from_sym(f_sym, f_ld, f_h): def gen_ld_h_from_sym(f_sym, f_ld, f_h):
f_ld.write("/* Variable definitions for ESP32ULP linker\n"); f_ld.write("/* Variable definitions for ESP32ULP linker\n")
f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n"); f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n")
f_ld.write(" */\n\n"); f_ld.write(" */\n\n")
f_h.write("// Variable definitions for ESP32ULP\n"); f_h.write("// Variable definitions for ESP32ULP\n")
f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n"); f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n")
f_h.write("#pragma once\n\n"); f_h.write("#pragma once\n\n")
for line in f_sym: for line in f_sym:
name, _, addr_str = line.split() name, _, addr_str = line.split()
addr = int(addr_str, 16) + BASE_ADDR; addr = int(addr_str, 16) + BASE_ADDR
f_h.write("extern uint32_t ulp_{0};\n".format(name)); f_h.write("extern uint32_t ulp_{0};\n".format(name))
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr)) f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
def main(): def main():
description = ("This application generates .h and .ld files for symbols defined in input file. " description = ("This application generates .h and .ld files for symbols defined in input file. "
"The input symbols file can be generated using nm utility like this: " "The input symbols file can be generated using nm utility like this: "
"esp32-ulp-nm -g -f posix <elf_file> > <symbols_file>" ); "esp32-ulp-nm -g -f posix <elf_file> > <symbols_file>")
parser = OptionParser(description=description) parser = OptionParser(description=description)
parser.add_option("-s", "--symfile", dest="symfile", parser.add_option("-s", "--symfile", dest="symfile",
@ -44,11 +45,10 @@ def main():
parser.print_help() parser.print_help()
return 1 return 1
with open(options.outputfile + ".h", 'w') as f_h, \ with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym:
open(options.outputfile + ".ld", 'w') as f_ld, \
open(options.symfile) as f_sym: \
gen_ld_h_from_sym(f_sym, f_ld, f_h) gen_ld_h_from_sym(f_sym, f_ld, f_h)
return 0 return 0
if __name__ == "__main__": if __name__ == "__main__":
exit(main()); exit(main())