Merge branch 'ag_make_fixes' into 'master'

This commit is contained in:
Angus Gratton 2016-08-19 14:29:06 +08:00
commit 1643bab9c5
11 changed files with 220 additions and 821 deletions

134
README.md
View File

@ -8,27 +8,30 @@
# Compiling your project
`make app`
`make all`
# Flashing the Bootloader
... will compile app, bootloader and generate a partition table based on the config.
ESP32 has a bootloader in ROM which runs after reset, but ESP-IDF also uses a second stage software bootloader. The ROM bootloader loads the software bootloader, which then loads the firmware app of the ESP32. The software bootloader must be flashed to offset 0x5000 in the flash.
# Flashing your project
To build the software bootloader, navigate to your project's top-level directory and run:
When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running:
``` shell
make bootloader
```
`make flash`
If you've configured the serial port details in `make menuconfig`, then
This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `make menuconfig`.
``` shell
make bootloader-flash
```
You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it.
... will automatically run esptool.py to flash the image. Otherwise, you can customise the `esptool.py` command that is printed out as part of `make bootloader`.
# Compiling & Flashing Just the App
You only need to flash the ESP32 bootloader once.
After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table:
* `make app` - build just the app.
* `make app-flash` - flash just the app.
`make app-flash` will automatically rebuild the app if it needs it.
(There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.)
# The Partition Table
@ -45,107 +48,4 @@ The simplest way to use the partition table is to `make menuconfig` and choose o
In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table.
Here is the summary printed for the "Single factory app, no OTA" configuration:
```
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
rfdata, data, rf, 0x110000, 256K
wifidata,data, wifi, 0x150000, 256K
```
* At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default.
* There are also two data regions defined in the partition table for storing RF & Wifi calibration data.
Here is the summary printed for the "Factory app, two OTA definitions" configuration:
```
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, 0x110000, 1M
ota_1, app, ota_1, 0x210000, 1M
rfdata, data, rf, 0x310000, 256K
wifidata,data, wifi, 0x350000, 256K
otadata, data, ota, 0x390000, 256K
```
* There are now three app partition definitions.
* The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps.
* There is also a new "ota data" slot, which holds the data for OTA updates. The bootloader consults this data in order to know which app to execute. If "ota data" is empty, it will execute the factory app.
## Creating Custom Tables
If you choose "Custom partition table CSV" in menuconfig then you can also enter the name of a CSV file (in the project directory) to use for your partition table. The CSV file can describe any number of definitions for the table you need.
The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table:
```
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
rfdata, data, rf, , 256K
wifidata, data, wifi, , 256K
otadata, data, ota, , 256K
```
* Whitespace between fields is ignored, and so is any line starting with # (comments).
* Each non-comment line in the CSV file is a partition definition.
* Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition.
### Name field
Name field can be any meaningful name. It is not significant to the ESP32. Names longer than 16 characters will be truncated.
### Type field
Type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for Espressif. If your application needs to store data, please add a custom partition type in the range 0x40-0xFE.
The bootloader ignores any types other than 0 & 1.
### Subtype
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot
When type is "data", the subtype field can be specified as ota (0), rf (1), wifi (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Other "data" subtypes are reserved for Espressif use. To create custom data partition subtypes then use a custom type value, and choose any subtype 0x00-0xFF.
### Offset & Size
Only the first offset field is required (we recommend using 0x10000). Partitions with blank offsets will start after the previous partition.
App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the offset field blank, the tool will automatically align the partition. If you specify an unaligned offset for an app partition, the tool will return an error.
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes).
## Generating Binary Partition Table
The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool components/partition_table/gen_esp32part.py is used to convert between CSV and binary formats.
If you configure the partition table CSV name in `make menuconfig` and then `make partition_table`, this conversion is done for you.
To convert CSV to Binary manually:
``` shell
python components/partition_table/gen_esp32part.py --verify input_partitions.csv binary_partitions.bin
```
To convert binary format back to CSV:
``` shell
python components/partition_table/gen_esp32part.py --verify binary_partitions.bin input_partitions.csv
```
To display the contents of a binary partition table on stdout (this is how the summaries displayed when running `make partition_table` are generated:
``` shell
python components/partition_table/gen_esp32part.py binary_partitions.bin
```
`gen_esp32part.py` takes one optional argument, `--verify`, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)
# Flashing the partition table
The command `make partition_table-flash` will flash the partition table with esptool.py. However a manual flashing command is printed as part of `make partition_table`.
For more details about partition tables and how to create custom variations, view the `docs/partition_tables.rst` file.

View File

@ -10,30 +10,34 @@
#
BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH)
EXTRA_CLEAN_TARGETS+=bootloader-clean
BOOTLOADER_BIN=$(BUILD_DIR_BASE)/bootloader.bin
BOOTLOADER_BUILD_DIR=$(BUILD_DIR_BASE)/bootloader
BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
.PHONY: bootloader-clean bootloader-flash bootloader
$(BOOTLOADER_BIN): $(COMPONENT_PATH)/src/sdkconfig
$(Q) PROJECT_PATH= \
COMPONENT_LDFLAGS= \
COMPONENT_INCLUDES= \
LDFLAGS= \
CFLAGS= \
BUILD_DIR_BASE=$(BUILD_DIR_BASE)/bootloader \
make -C $(BOOTLOADER_COMPONENT_PATH)/src MAKEFLAGS= V=$(V) TARGET_BIN_LAYOUT="$(BOOTLOADER_TARGET_BIN_LAYOUT)"
BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \
$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src MAKEFLAGS= V=$(V) TARGET_BIN_LAYOUT="$(BOOTLOADER_TARGET_BIN_LAYOUT)" $(BOOTLOADER_BIN)
bootloader-clean:
$(Q) PROJECT_PATH= \
COMPONENT_LDFLAGS= \
COMPONENT_INCLUDES= \
LDFLAGS= \
CFLAGS= \
BUILD_DIR_BASE=$(BUILD_DIR_BASE)/bootloader \
make -C $(BOOTLOADER_COMPONENT_PATH)/src clean MAKEFLAGS= V=$(V)
BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \
$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src app-clean MAKEFLAGS= V=$(V)
bootloader: $(BUILD_DIR_BASE)/bootloader.bin
clean: bootloader-clean
bootloader: $(BOOTLOADER_BIN)
@echo "Bootloader built. Default flash command is:"
@echo "$(ESPTOOLPY_SERIAL) write_flash 0x1000 $(BOOTLOADER_BIN)"
all_binaries: $(BOOTLOADER_BIN)
ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN)
# synchronise the project level config to the component's
# config
@ -42,4 +46,4 @@ $(COMPONENT_PATH)/src/sdkconfig: $(PROJECT_PATH)/sdkconfig
# bootloader-flash calls flash in the bootloader dummy project
bootloader-flash: $(BOOTLOADER_BIN)
make -C $(BOOTLOADER_COMPONENT_PATH)/src flash MAKEFLAGS= V=$(V) CONFIG_APP_OFFSET=0x1000
$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src flash MAKEFLAGS= V=$(V)

View File

@ -12,14 +12,15 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON))
ESPTOOLPY := $(PYTHON) $(SDK_PATH)/bin/esptool.py --chip esp32
ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD)
PROJECT_FLASH_COMMAND=$(ESPTOOLPY_SERIAL) write_flash $(CONFIG_APP_OFFSET) $(PROJECT_BIN)
APP_FLASH_COMMAND=$(ESPTOOLPY_SERIAL) write_flash $(CONFIG_APP_OFFSET) $(APP_BIN)
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN)
$(PROJECT_BIN): $(PROJECT_ELF)
$(APP_BIN): $(APP_ELF)
$(Q) $(ESPTOOLPY) elf2image -o $@ $<
flash: $(PROJECT_BIN)
flash: all_binaries
@echo "Flashing project app to $(CONFIG_APP_OFFSET)..."
$(Q) $(PROJECT_FLASH_COMMAND)
$(Q) $(ESPTOOLPY_SERIAL) write_flash $(ESPTOOL_ALL_FLASH_ARGS)
# convenience target to flash bootloader, partitions, app all at once
flash_all: bootloader-flash partition_table-flash flash
app-flash: $(APP_BIN)
$(Q) $(APP_FLASH_COMMAND)

View File

@ -8,8 +8,6 @@
#
.PHONY: partition_table partition_table-flash partition_table-clean
EXTRA_CLEAN_TARGETS+=partition_table-clean
# NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir
GEN_ESP32PART := $(PYTHON) $(SDK_PATH)/bin/gen_esp32part.py -q
@ -24,7 +22,10 @@ $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH)
@echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..."
$(Q) $(GEN_ESP32PART) $< $@
all_binaries: $(PARTITION_TABLE_BIN)
PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash 0x4000 $(PARTITION_TABLE_BIN)
ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN)
partition_table: $(PARTITION_TABLE_BIN)
@echo "Partition table binary generated. Contents:"
@ -40,3 +41,5 @@ partition_table-flash: $(PARTITION_TABLE_BIN)
partition_table-clean:
$(Q) rm -f $(PARTITION_TABLE_BIN)
clean: partition_table-clean

View File

@ -21,7 +21,7 @@ Import New Project
* Once Eclipse is running, choose File -> Import...
* In the dialog that pops up, choose "C/C++" -> "EXisting Code as Makefile Project" and click Next.
* In the dialog that pops up, choose "C/C++" -> "Existing Code as Makefile Project" and click Next.
* On the next page, enter "Existing Code Location" to be the directory of your SDK project. Don't specify the path to the SDK itself.
@ -39,7 +39,7 @@ Project Properties
*Windows users only, follow these two additional steps:*
* On the same Environment property page, edit the PATH environment variable and append ";C:\msys32\usr\bin;C:\msys32\mingw32\bin;C:\msys32\opt\xtensa-esp32-elf\bin" to the end of the default value. (If you installed msys32 to a different directory then you'll need to change these paths to match.)
* On the same Environment property page, edit the PATH environment variable and append ``;C:\msys32\usr\bin;C:\msys32\mingw32\bin;C:\msys32\opt\xtensa-esp32-elf\bin`` to the end of the default value. (If you installed msys32 to a different directory then you'll need to change these paths to match.)
* Click on the "C/C++ Build" top-level properties page then uncheck "Use default build command" and enter this for the custom build command: ``bash ${SDK_PATH}/bin/eclipse_windows_make.sh``.
@ -60,7 +60,7 @@ Flash from Eclipse
You can integrate the "make flash" target into your Eclipse project to flash using esptool.py from the Eclipse UI:
* Right-click the "esp-idf-tests" project in Project Explorer (important to make sure you don't select a subdirectory of the project or Eclipse may find the wrong Makefile.)
* Right-click your project in Project Explorer (important to make sure you don't select a subdirectory of the project or Eclipse may find the wrong Makefile.)
* Select Make Targets -> Create from the context menu.

119
docs/partition-tables.rst Normal file
View File

@ -0,0 +1,119 @@
Partition Tables
----------------
A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash.
Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables:
* "Single factory app, no OTA"
* "Factory app, two OTA definitions"
In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table.
Built-in Partition Tables
-------------------------
Here is the summary printed for the "Single factory app, no OTA" configuration::
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
rfdata, data, rf, 0x110000, 256K
wifidata,data, wifi, 0x150000, 256K
* At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default.
* There are also two data regions defined in the partition table for storing RF & Wifi calibration data.
Here is the summary printed for the "Factory app, two OTA definitions" configuration::
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, 0x110000, 1M
ota_1, app, ota_1, 0x210000, 1M
rfdata, data, rf, 0x310000, 256K
wifidata,data, wifi, 0x350000, 256K
otadata, data, ota, 0x390000, 256K
* There are now three app partition definitions.
* The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps.
* There is also a new "ota data" slot, which holds the data for OTA updates. The bootloader consults this data in order to know which app to execute. If "ota data" is empty, it will execute the factory app.
Creating Custom Tables
----------------------
If you choose "Custom partition table CSV" in menuconfig then you can also enter the name of a CSV file (in the project directory) to use for your partition table. The CSV file can describe any number of definitions for the table you need.
The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table::
# Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
rfdata, data, rf, , 256K
wifidata, data, wifi, , 256K
otadata, data, ota, , 256K
* Whitespace between fields is ignored, and so is any line starting with # (comments).
* Each non-comment line in the CSV file is a partition definition.
* Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition.
Name field
~~~~~~~~~~
Name field can be any meaningful name. It is not significant to the ESP32. Names longer than 16 characters will be truncated.
Type field
~~~~~~~~~~
Type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for Espressif. If your application needs to store data, please add a custom partition type in the range 0x40-0xFE.
The bootloader ignores any types other than 0 & 1.
Subtype
~~~~~~~
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot
When type is "data", the subtype field can be specified as ota (0), rf (1), wifi (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Other "data" subtypes are reserved for Espressif use. To create custom data partition subtypes then use a custom type value, and choose any subtype 0x00-0xFF.
Offset & Size
~~~~~~~~~~~~~
Only the first offset field is required (we recommend using 0x10000). Partitions with blank offsets will start after the previous partition.
App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the offset field blank, the tool will automatically align the partition. If you specify an unaligned offset for an app partition, the tool will return an error.
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes).
Generating Binary Partition Table
---------------------------------
The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool bin/gen_esp32part.py is used to convert between CSV and binary formats.
If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done for you.
To convert CSV to Binary manually::
python bin/gen_esp32part.py --verify input_partitions.csv binary_partitions.bin
To convert binary format back to CSV::
python bin/gen_esp32part.py --verify binary_partitions.bin input_partitions.csv
To display the contents of a binary partition table on stdout (this is how the summaries displayed when running `make partition_table` are generated::
python bin/gen_esp32part.py binary_partitions.bin
``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)
Flashing the partition table
----------------------------
* ``make partition_table-flash``: will flash the partition table with esptool.py.
* ``make flash``: Will flash everything including the partition table.
A manual flashing command is also printed as part of ``make partition_table``.

View File

@ -83,5 +83,6 @@ Use ``cd`` to change to the project directory (not the SDK directory.) Type ``ma
If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory.
.. _Eclipse: eclipse-setup.rst
.. _MSYS2: https://msys2.github.io/
.. _github: https://github.com/espressif/esp-idf-template

View File

@ -10,18 +10,41 @@
# Makefile is located.
#
.PHONY: build-components menuconfig all build clean
all: project
.PHONY: build-components menuconfig all build clean all_binaries
all: all_binaries # other components will add dependencies to 'all_binaries'
@echo "To flash all build output, run 'make flash' or:"
@echo $(ESPTOOLPY_SERIAL) write_flash $(ESPTOOL_ALL_FLASH_ARGS)
# (the reason all_binaries is used instead of 'all' is so that the flash target
# can build everything without triggering the per-component "to flash..."
# output targets.)
help:
@echo "Welcome to Espressif IDF build system. Some useful make targets:"
@echo ""
@echo "make menuconfig - Configure IDF project"
@echo ""
@echo "make all - Build app, bootloader, partition table"
@echo "make flash - Flash all components to a fresh chip"
@echo "make clean - Remove all build output"
@echo ""
@echo "make app - Build just the app"
@echo "make app-flash - Flash just the app"
@echo "make app-clean - Clean just the app"
@echo ""
@echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', "
@echo "'make partition_table', etc, etc."
# disable built-in make rules, makes debugging saner
MAKEFLAGS +=-rR
# Figure out PROJECT_PATH if not set, check for unacceptable Makefile entry points
# Figure out PROJECT_PATH if not set
ifeq ("$(PROJECT_PATH)","")
#The path to the project: we assume the Makefile including this file resides
#in the root of that directory.
PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
export PROJECT_PATH
endif
#The directory where we put all objects/libraries/binaries. The project Makefile can
#configure this if needed.
@ -59,26 +82,26 @@ COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(c
# Assemble global list of include dirs (COMPONENT_INCLUDES), and
# LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component.
COMPONENT_INCLUDES :=
COMPONENT_LDFLAGS :=
#
# Also add any inter-component dependencies for each component.
# Extract a variable from a child make process
#
# $(1) - path to directory to invoke make in
# $(2) - name of variable to print via the getvariable target (passed in GET_VARIABLE)
# $(2) - name of variable to print via the get_variable target (passed in GET_VARIABLE)
#
# needs 'sed' processing of stdout because make sometimes echoes other stuff on stdout,
# even if asked not to.
#
# Debugging this? Replace $(shell with $(error and you'll see the full command as-run.
define GetVariable
$(shell "$(MAKE)" -s --no-print-directory -C $(1) get_variable COMPONENT_INCLUDES=dummy COMPONENT_LDFLAGS=dummy PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" )
$(shell "$(MAKE)" -s --no-print-directory -C $(1) get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" )
endef
ifeq ("$(COMPONENT_INCLUDES)","")
COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \
$(call GetVariable,$(comp),COMPONENT_ADD_INCLUDEDIRS))))
endif
#Also add project include path, for sdk includes
COMPONENT_INCLUDES += $(PROJECT_PATH)/build/include/
@ -86,11 +109,9 @@ export COMPONENT_INCLUDES
#COMPONENT_LDFLAGS has a list of all flags that are needed to link the components together. It's collected
#in the same way as COMPONENT_INCLUDES is.
ifeq ("$(COMPONENT_LDFLAGS)","")
COMPONENT_LDFLAGS := $(foreach comp,$(COMPONENT_PATHS_BUILDABLE), \
$(call GetVariable,$(comp),COMPONENT_ADD_LDFLAGS))
export COMPONENT_LDFLAGS
endif
# Generate component dependency targets from dependencies lists
# each component gains a target of its own <name>-build with dependencies
@ -128,8 +149,10 @@ export CC CXX LD AR OBJCOPY
PYTHON=$(call dequote,$(CONFIG_PYTHON))
PROJECT_ELF:=$(BUILD_DIR_BASE)/$(PROJECT_NAME).elf
PROJECT_BIN:=$(PROJECT_ELF:.elf=.bin)
# the app is the main executable built by the project
APP_ELF:=$(BUILD_DIR_BASE)/$(PROJECT_NAME).elf
APP_MAP:=$(APP_ELF:.elf=.map)
APP_BIN:=$(APP_ELF:.elf=.bin)
# Include any Makefile.projbuild file letting components add
# configuration at the project level
@ -139,17 +162,21 @@ COMPONENT_PATH := $(1)
endef
$(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath))))
# once we know component paths, we can include the config
include $(SDK_PATH)/make/project_config.mk
# ELF depends on the -build target of every component
$(PROJECT_ELF): $(addsuffix -build,$(notdir $(COMPONENT_PATHS_BUILDABLE)))
$(APP_ELF): $(addsuffix -build,$(notdir $(COMPONENT_PATHS_BUILDABLE)))
$(vecho) LD $(notdir $@)
$(Q) $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(patsubst %.elf,%.map,$@)
$(Q) $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP)
# Generation of $(PROJECT_BIN) from $(PROJECT_ELF) is added by the esptool
# Generation of $(APP_BIN) from $(APP_ELF) is added by the esptool
# component's Makefile.projbuild
project: $(PROJECT_BIN)
app: $(APP_BIN)
@echo "App built. Default flash app command is:"
@echo $(PROJECT_FLASH_COMMAND) # PROJECT_FLASH_COMMAND is set in esptool_py's Makefile.projbuild
@echo $(APP_FLASH_COMMAND) # APP_FLASH_COMMAND is set in esptool_py's Makefile.projbuild
all_binaries: $(APP_BIN)
$(BUILD_DIR_BASE):
mkdir -p $(BUILD_DIR_BASE)
@ -174,12 +201,10 @@ $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponent
$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTarget,$(component),build,$(PROJECT_PATH)/build/include/sdkconfig.h)))
$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTarget,$(component),clean)))
include $(SDK_PATH)/make/project_config.mk
app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE)))
$(vecho) RM $(APP_ELF)
$(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP)
clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(EXTRA_CLEAN_TARGETS)
$(vecho) RM $(PROJECT_ELF)
$(Q) rm -f $(PROJECT_ELF)
$(Q) rm -rf $(PROJECT_PATH)/build/include/config $(PROJECT_PATH)/build/include/sdkconfig.h
clean: app-clean
endif

View File

@ -12,6 +12,7 @@ $(KCONFIG_TOOL_DIR)/mconf $(KCONFIG_TOOL_DIR)/conf:
MAKEFLAGS="" \
CFLAGS="" \
LDFLAGS="" \
CC=$(HOSTCC) LD=$(HOSTLD) \
$(MAKE) -C $(KCONFIG_TOOL_DIR)
menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(SDK_PATH)/Kconfig $(BUILD_DIR_BASE)
@ -30,7 +31,7 @@ endif
# Work out of whether we have to build the Kconfig makefile
# (auto.conf), or if we're in a situation where we don't need it
NON_CONFIG_TARGETS := clean %-clean get_variable
NON_CONFIG_TARGETS := clean %-clean get_variable help
AUTO_CONF_REGEN_TARGET := $(PROJECT_PATH)/build/include/config/auto.conf
# disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets
@ -58,6 +59,7 @@ $(AUTO_CONF_REGEN_TARGET) $(PROJECT_PATH)/build/include/sdkconfig.h: $(PROJECT_P
clean: config-clean
.PHONY: config-clean
EXTRA_CLEAN_TARGETS += config-clean
config-clean:
$(vecho RM CONFIG)
$(MAKE) -C $(KCONFIG_TOOL_DIR) clean
$(Q) rm -rf $(PROJECT_PATH)/build/include/config $(PROJECT_PATH)/build/include/sdkconfig.h

View File

@ -1,343 +0,0 @@
#!/usr/bin/env python
#
# ESP32 partition table generation tool
#
# Takes partition input from a CSV file of this format:
#
# Name,Type,SubType,Offset,Size
#
# Any row which starts with # is ignored as a comment. Whitespace at beginning/end of field is ignored.
#
# EXAMPLE CSV FILE CONTENTS
# -------------------------
"""
# Name, Type, SubType, Offset, Size
factory, 0, 0, , 1M
ota_0, 0, 0x11, , 1M
ota_1, 0, 0x12, , 1M
ota_data, 1, 0, 5M, 128K
"""
#
# SAMPLE USAGE
# ------------
#
# Convert CSV layout to binary:
# gen_esp32part.py table.csv table.bin
#
# Convert binary to CSV, print on stdout:
# gen_esp32part.py table.bin
#
# FIELD DETAILS
# -------------
#
# Name: Human-readable name of partition. Values longer than 16 bytes will be truncated.
#
# Type: Numeric value for type field, or special keywords 'app' (for 0x00) or 'data' (for 0x01).
#
# SubType: Numeric value for subtype field, or special keywords depending on known types:
# app (0x00) : factory, ota_0 through ota_15, test.
# data (0x01) : ota, rf, wifi
#
# Default is zero if left blank.
#
#
# Offset: Start of partition. Examples: 0x800, 0x10000, 512, 2K, 1M
# - Offset can be left blank, and gen_esp32part will find the next valid offset (aligned to 64KB for app partitions.)
#
# Size: Size of partition. Examples: 0x800, 0x10000, 512, 2K, 1M
#
# SubType: 0
#
# Offset: End of previous partition, aligned to 64kB if Type = 0 (app) & 4 bytes otherwise.
#
#
#
import struct
import argparse
import sys
__version__ = '1.0'
def debug(msg):
""" Print to stderr """
sys.stderr.write(msg)
sys.stderr.write('\n')
class PartitionTable(list):
def __init__(self):
super(PartitionTable, self).__init__(self)
@classmethod
def from_csv(cls, csv_contents):
res = PartitionTable()
lines = csv_contents.split("\n")
for line_no in range(len(lines)):
line = lines[line_no].strip()
if line.startswith("#") or len(line) == 0:
continue
try:
res.append(PartitionDefinition.from_csv(line))
except InputError as e:
raise InputError("Error at line %d: %s" % (line_no+1, e))
except Exception:
debug("Unexpected error parsing line %d: %s" % (line_no+1, line))
raise
# fix up missing offsets & negative sizes
last_end = 0x5000 # first offset after partition table
for e in res:
if e.offset is None:
pad_to = 0x10000 if e.type == PartitionDefinition.APP_TYPE else 4
if last_end % pad_to != 0:
last_end += pad_to - (last_end % pad_to)
e.offset = last_end
if e.size < 0:
e.size = -e.size - e.offset
last_end = e.offset + e.size
return res
def __getitem__(self, item):
""" Allow partition table access via name as well as by
numeric index. """
if isinstance(item, str):
for x in self:
if x.name == item:
return x
raise ValueError("No partition entry named '%s'" % item)
else:
return super(PartitionTable, self).__getitem__(item)
def verify(self):
# verify each partition individually
for p in self:
p.verify()
# check for overlaps
last = None
for p in sorted(self):
if p.offset < 0x5000:
raise InputError("Partition offset 0x%x is below 0x5000" % p.offset)
if last is not None and p.offset < last.offset + last.size:
raise InputError("Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset, last.offset, last.offset+last.size-1))
last = p
@classmethod
def from_binary(cls, b):
if len(b) % 32 != 0:
raise InputError("Partition table length must be a multiple of 32 bytes. Got %d bytes." % len(b))
result = cls()
for o in range(0,len(b),32):
result.append(PartitionDefinition.from_binary(b[o:o+32]))
return result
def to_binary(self):
return "".join(e.to_binary() for e in self)
def to_csv(self, simple_formatting=False):
rows = [ "# Espressif ESP32 Partition Table",
"# Name, Type, SubType, Offset, Size" ]
rows += [ x.to_csv(simple_formatting) for x in self ]
return "\n".join(rows)
class PartitionDefinition(object):
APP_TYPE = 0x00
DATA_TYPE = 0x01
TYPES = {
"app" : APP_TYPE,
"data" : DATA_TYPE,
}
SUBTYPES = {
APP_TYPE : {
"factory" : 0x00,
"test" : 0x20,
},
DATA_TYPE : {
"ota" : 0x00,
"rf" : 0x01,
"wifi" : 0x02,
},
}
MAGIC_BYTES = "\xAA\x50"
ALIGNMENT = {
APP_TYPE : 0x1000,
DATA_TYPE : 0x04,
}
# add subtypes for the 16 OTA slot values ("ota_XXX, etc.")
for ota_slot in range(16):
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot
def __init__(self):
self.name = ""
self.type = None
self.subtype = None
self.offset = None
self.size = None
@classmethod
def from_csv(cls, line):
""" Parse a line from the CSV """
line_w_defaults = line + ",,," # lazy way to support default fields
fields = [ f.strip() for f in line_w_defaults.split(",") ]
res = PartitionDefinition()
res.name = fields[0]
res.type = res.parse_type(fields[1])
res.subtype = res.parse_subtype(fields[2])
res.offset = res.parse_address(fields[3])
res.size = res.parse_address(fields[4])
if res.size is None:
raise InputError("Size field can't be empty")
return res
def __eq__(self, other):
return self.name == other.name and self.type == other.type \
and self.subtype == other.subtype and self.offset == other.offset \
and self.size == other.size
def __repr__(self):
def maybe_hex(x):
return "0x%x" % x if x is not None else "None"
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type, self.subtype or 0,
maybe_hex(self.offset), maybe_hex(self.size))
def __str__(self):
return "Part '%s' %d/%d @ 0x%x size 0x%x" % (self.name, self.type, self.subtype, self.offset or -1, self.size or -1)
def __cmp__(self, other):
return self.offset - other.offset
def parse_type(self, strval):
if strval == "":
raise InputError("Field 'type' can't be left empty.")
return parse_int(strval, self.TYPES)
def parse_subtype(self, strval):
if strval == "":
return 0 # default
return parse_int(strval, self.SUBTYPES.get(self.type, {}))
def parse_address(self, strval):
if strval == "":
return None # PartitionTable will fill in default
return parse_int(strval)
def verify(self):
if self.type is None:
raise ValidationError("Type field is not set")
if self.subtype is None:
raise ValidationError("Subtype field is not set")
if self.offset is None:
raise ValidationError("Offset field is not set")
align = self.ALIGNMENT.get(self.type, 4)
if self.offset % align:
raise ValidationError("%s offset 0x%x is not aligned to 0x%x" % (self.name, self.offset, align))
if self.size is None:
raise ValidationError("Size field is not set")
STRUCT_FORMAT = "<2sBBLL16sL"
@classmethod
def from_binary(cls, b):
if len(b) != 32:
raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b))
res = cls()
(magic, res.type, res.subtype, res.offset,
res.size, res.name, reserved) = struct.unpack(cls.STRUCT_FORMAT, b)
if "\x00" in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index("\x00")]
if magic != cls.MAGIC_BYTES:
raise InputError("Invalid magic bytes (%r) for partition definition" % magic)
if reserved != 0:
debug("WARNING: Partition definition had unexpected reserved value 0x%08x. Newer binary format?" % reserved)
return res
def to_binary(self):
return struct.pack(self.STRUCT_FORMAT,
self.MAGIC_BYTES,
self.type, self.subtype,
self.offset, self.size,
self.name,
0) # reserved
def to_csv(self, simple_formatting=False):
def addr_format(a):
if not simple_formatting:
for (val, suffix) in [ (0x100000, "M"), (0x400, "K") ]:
if a % val == 0:
return "%d%s" % (a / val, suffix)
return "0x%x" % a
def lookup_keyword(t, keywords):
for k,v in keywords.items():
if simple_formatting == False and t == v:
return k
return "%d" % t
return ",".join([ self.name,
lookup_keyword(self.type, self.TYPES),
lookup_keyword(self.subtype, self.SUBTYPES),
addr_format(self.offset),
addr_format(self.size) ])
class InputError(RuntimeError):
def __init__(self, e):
super(InputError, self).__init__(e)
def parse_int(v, keywords={}):
"""Generic parser for integer fields - int(x,0) with provision for
k/m/K/M suffixes and 'keyword' value lookup.
"""
try:
for letter, multiplier in [ ("k",1024), ("m",1024*1024) ]:
if v.lower().endswith(letter):
return parse_int(v[:-1], keywords) * multiplier
return int(v, 0)
except ValueError:
if len(keywords) == 0:
raise InputError("Invalid field value %s" % v)
try:
return keywords[v.lower()]
except KeyError:
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
def main():
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
parser.add_argument('--verify', '-v', help='Verify partition table fields', default=True, action='store_false')
parser.add_argument('input', help='Path to CSV or binary file to parse. Will use stdin if omitted.', type=argparse.FileType('r'), default=sys.stdin)
parser.add_argument('output', help='Path to output converted binary or CSV file. Will use stdout if omitted, unless the --display argument is also passed (in which case only the summary is printed.)',
type=argparse.FileType('w'), nargs='?',
default=sys.stdout)
args = parser.parse_args()
input = args.input.read()
input_is_binary = input[0:2] == PartitionDefinition.MAGIC_BYTES
if input_is_binary:
debug("Parsing binary partition input...")
table = PartitionTable.from_binary(input)
else:
debug("Parsing CSV input...")
table = PartitionTable.from_csv(input)
if args.verify:
debug("Verifying table...")
table.verify()
if input_is_binary:
output = table.to_csv()
else:
output = table.to_binary()
args.output.write(output)
if __name__ == '__main__':
try:
main()
except InputError as e:
print(e)
sys.exit(2)

View File

@ -1,313 +0,0 @@
#!/usr/bin/env python
import unittest
import struct
import csv
import sys
import subprocess
import tempfile
import os
sys.path.append("..")
from gen_esp32part import *
SIMPLE_CSV = """
# Name,Type,SubType,Offset,Size
factory,0,2,65536,1048576
"""
LONGER_BINARY_TABLE = ""
# type 0x00, subtype 0x00,
# offset 64KB, size 1MB
LONGER_BINARY_TABLE += "\xAA\x50\x00\x00" + \
"\x00\x00\x01\x00" + \
"\x00\x00\x10\x00" + \
"factory\0" + ("\0"*8) + \
"\x00\x00\x00\x00"
# type 0x01, subtype 0x20,
# offset 0x110000, size 128KB
LONGER_BINARY_TABLE += "\xAA\x50\x01\x20" + \
"\x00\x00\x11\x00" + \
"\x00\x02\x00\x00" + \
"data" + ("\0"*12) + \
"\x00\x00\x00\x00"
# type 0x10, subtype 0x00,
# offset 0x150000, size 1MB
LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \
"\x00\x00\x15\x00" + \
"\x00\x10\x00\x00" + \
"second" + ("\0"*10) + \
"\x00\x00\x00\x00"
class CSVParserTests(unittest.TestCase):
def test_simple_partition(self):
table = PartitionTable.from_csv(SIMPLE_CSV)
self.assertEqual(len(table), 1)
self.assertEqual(table[0].name, "factory")
self.assertEqual(table[0].type, 0)
self.assertEqual(table[0].subtype, 2)
self.assertEqual(table[0].offset, 65536)
self.assertEqual(table[0].size, 1048576)
def test_require_type(self):
csv = """
# Name,Type, SubType,Offset,Size
ihavenotype,
"""
with self.assertRaisesRegexp(InputError, "type"):
PartitionTable.from_csv(csv)
def test_type_subtype_names(self):
csv_magicnumbers = """
# Name, Type, SubType, Offset, Size
myapp, 0, 0,, 0x100000
myota_0, 0, 0x10,, 0x100000
myota_1, 0, 0x11,, 0x100000
myota_15, 0, 0x1f,, 0x100000
mytest, 0, 0x20,, 0x100000
myota_status, 1, 0,, 0x100000
"""
csv_nomagicnumbers = """
# Name, Type, SubType, Offset, Size
myapp, app, factory,, 0x100000
myota_0, app, ota_0,, 0x100000
myota_1, app, ota_1,, 0x100000
myota_15, app, ota_15,, 0x100000
mytest, app, test,, 0x100000
myota_status, data, ota,, 0x100000
"""
# make two equivalent partition tables, one using
# magic numbers and one using shortcuts. Ensure they match
magic = PartitionTable.from_csv(csv_magicnumbers)
magic.verify()
nomagic = PartitionTable.from_csv(csv_nomagicnumbers)
nomagic.verify()
self.assertEqual(nomagic["myapp"].type, 0)
self.assertEqual(nomagic["myapp"].subtype, 0)
self.assertEqual(nomagic["myapp"], magic["myapp"])
self.assertEqual(nomagic["myota_0"].type, 0)
self.assertEqual(nomagic["myota_0"].subtype, 0x10)
self.assertEqual(nomagic["myota_0"], magic["myota_0"])
self.assertEqual(nomagic["myota_15"], magic["myota_15"])
self.assertEqual(nomagic["mytest"], magic["mytest"])
self.assertEqual(nomagic["myota_status"], magic["myota_status"])
#self.assertEqual(nomagic.to_binary(), magic.to_binary())
def test_unit_suffixes(self):
csv = """
# Name, Type, Subtype, Offset, Size
one_megabyte, app, factory, 32k, 1M
"""
t = PartitionTable.from_csv(csv)
t.verify()
self.assertEqual(t[0].offset, 32*1024)
self.assertEqual(t[0].size, 1*1024*1024)
def test_default_offsets(self):
csv = """
# Name, Type, Subtype, Offset, Size
first, app, factory,, 1M
second, data, 0x15,, 1M
minidata, data, 0x40,, 32K
otherapp, app, factory,, 1M
"""
t = PartitionTable.from_csv(csv)
# 'first'
self.assertEqual(t[0].offset, 0x010000) # 64KB boundary as it's an app image
self.assertEqual(t[0].size, 0x100000) # Size specified in CSV
# 'second'
self.assertEqual(t[1].offset, 0x110000) # prev offset+size
self.assertEqual(t[1].size, 0x100000) # Size specified in CSV
# 'minidata'
self.assertEqual(t[2].offset, 0x210000)
# 'otherapp'
self.assertEqual(t[3].offset, 0x220000) # 64KB boundary as it's an app image
def test_negative_size_to_offset(self):
csv = """
# Name, Type, Subtype, Offset, Size
first, app, factory, 0x10000, -2M
second, data, 0x15, , 1M
"""
t = PartitionTable.from_csv(csv)
t.verify()
# 'first'
self.assertEqual(t[0].offset, 0x10000) # in CSV
self.assertEqual(t[0].size, 0x200000 - t[0].offset) # Up to 2M
# 'second'
self.assertEqual(t[1].offset, 0x200000) # prev offset+size
def test_overlapping_offsets_fail(self):
csv = """
first, app, factory, 0x100000, 2M
second, app, ota_0, 0x200000, 1M
"""
t = PartitionTable.from_csv(csv)
with self.assertRaisesRegexp(InputError, "overlap"):
t.verify()
class BinaryOutputTests(unittest.TestCase):
def test_binary_entry(self):
csv = """
first, 0x30, 0xEE, 0x100400, 0x300000
"""
t = PartitionTable.from_csv(csv)
tb = t.to_binary()
self.assertEqual(len(tb), 32)
self.assertEqual('\xAA\x50', tb[0:2]) # magic
self.assertEqual('\x30\xee', tb[2:4]) # type, subtype
eo, es = struct.unpack("<LL", tb[4:12])
self.assertEqual(eo, 0x100400) # offset
self.assertEqual(es, 0x300000) # size
def test_multiple_entries(self):
csv = """
first, 0x30, 0xEE, 0x100400, 0x300000
second,0x31, 0xEF, , 0x100000
"""
t = PartitionTable.from_csv(csv)
tb = t.to_binary()
self.assertEqual(len(tb), 64)
self.assertEqual('\xAA\x50', tb[0:2])
self.assertEqual('\xAA\x50', tb[32:34])
class BinaryParserTests(unittest.TestCase):
def test_parse_one_entry(self):
# type 0x30, subtype 0xee,
# offset 1MB, size 2MB
entry = "\xAA\x50\x30\xee" + \
"\x00\x00\x10\x00" + \
"\x00\x00\x20\x00" + \
"0123456789abc\0\0\0" + \
"\x00\x00\x00\x00"
# verify that parsing 32 bytes as a table
# or as a single Definition are the same thing
t = PartitionTable.from_binary(entry)
self.assertEqual(len(t), 1)
t[0].verify()
e = PartitionDefinition.from_binary(entry)
self.assertEqual(t[0], e)
e.verify()
self.assertEqual(e.type, 0x30)
self.assertEqual(e.subtype, 0xEE)
self.assertEqual(e.offset, 0x100000)
self.assertEqual(e.size, 0x200000)
self.assertEqual(e.name, "0123456789abc")
def test_multiple_entries(self):
t = PartitionTable.from_binary(LONGER_BINARY_TABLE)
t.verify()
self.assertEqual(3, len(t))
self.assertEqual(t[0].type, PartitionDefinition.APP_TYPE)
self.assertEqual(t[0].name, "factory")
self.assertEqual(t[1].type, PartitionDefinition.DATA_TYPE)
self.assertEqual(t[1].name, "data")
self.assertEqual(t[2].type, 0x10)
self.assertEqual(t[2].name, "second")
round_trip = t.to_binary()
self.assertEqual(round_trip, LONGER_BINARY_TABLE)
def test_bad_magic(self):
bad_magic = "OHAI" + \
"\x00\x00\x10\x00" + \
"\x00\x00\x20\x00" + \
"0123456789abc\0\0\0" + \
"\x00\x00\x00\x00"
with self.assertRaisesRegexp(InputError, "Invalid magic bytes"):
PartitionTable.from_binary(bad_magic)
def test_bad_length(self):
bad_length = "OHAI" + \
"\x00\x00\x10\x00" + \
"\x00\x00\x20\x00" + \
"0123456789"
with self.assertRaisesRegexp(InputError, "32 bytes"):
PartitionTable.from_binary(bad_length)
class CSVOutputTests(unittest.TestCase):
def test_output_simple_formatting(self):
table = PartitionTable.from_csv(SIMPLE_CSV)
as_csv = table.to_csv(True)
c = csv.reader(as_csv.split("\n"))
# first two lines should start with comments
self.assertEqual(c.next()[0][0], "#")
self.assertEqual(c.next()[0][0], "#")
row = c.next()
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "0")
self.assertEqual(row[2], "2")
self.assertEqual(row[3], "0x10000") # reformatted as hex
self.assertEqual(row[4], "0x100000") # also hex
# round trip back to a PartitionTable and check is identical
roundtrip = PartitionTable.from_csv(as_csv)
self.assertEqual(roundtrip, table)
def test_output_smart_formatting(self):
table = PartitionTable.from_csv(SIMPLE_CSV)
as_csv = table.to_csv(False)
c = csv.reader(as_csv.split("\n"))
# first two lines should start with comments
self.assertEqual(c.next()[0][0], "#")
self.assertEqual(c.next()[0][0], "#")
row = c.next()
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "app")
self.assertEqual(row[2], "2")
self.assertEqual(row[3], "64K")
self.assertEqual(row[4], "1M")
# round trip back to a PartitionTable and check is identical
roundtrip = PartitionTable.from_csv(as_csv)
self.assertEqual(roundtrip, table)
class CommandLineTests(unittest.TestCase):
def test_basic_cmdline(self):
try:
binpath = tempfile.mktemp()
csvpath = tempfile.mktemp()
# copy binary contents to temp file
with open(binpath, 'w') as f:
f.write(LONGER_BINARY_TABLE)
# run gen_esp32part.py to convert binary file to CSV
subprocess.check_call([sys.executable, "../gen_esp32part.py",
binpath, csvpath])
# reopen the CSV and check the generated binary is identical
with open(csvpath, 'r') as f:
from_csv = PartitionTable.from_csv(f.read())
self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE)
# run gen_esp32part.py to conver the CSV to binary again
subprocess.check_call([sys.executable, "../gen_esp32part.py",
csvpath, binpath])
# assert that file reads back as identical
with open(binpath, 'rb') as f:
binary_readback = f.read()
self.assertEqual(binary_readback, LONGER_BINARY_TABLE)
finally:
for path in binpath, csvpath:
try:
os.remove(path)
except OSError:
pass
if __name__ =="__main__":
unittest.main()