Update build system, add cspot service option

This commit is contained in:
Sebastien L
2021-12-28 12:43:29 -05:00
parent f3c405579f
commit b2ec1506d7
36 changed files with 847 additions and 115 deletions

View File

@@ -53,6 +53,7 @@ jobs:
branch_name="${branch_name//[^a-zA-Z0-9\-~!@_\.]/}"
BUILD_NUMBER=${{ needs.job1.outputs.build_number }}
echo "BUILD_NUMBER=${BUILD_NUMBER}" >> $GITHUB_ENV
echo "DOCKER_IMAGE_NAME=sle118/squeezelite-esp32-idfv4-master" >> $GITHUB_ENV
tag="${TARGET_BUILD_NAME}.${{matrix.depth}}.${BUILD_NUMBER}.${branch_name}"
echo "tag=${tag}" >> $GITHUB_ENV
last_commit="$(git log --pretty=format:'%s' --max-count=1)"
@@ -84,10 +85,12 @@ jobs:
run: |
env | grep "artifact\|tag\|GITHUB\|version\|NUMBER\|TARGET" >${TARGET_BUILD_NAME}-env.txt
echo "${tag}" >version.txt
echo pulling docker version 4.3.1
docker pull espressif/idf:v4.3.1
docker info
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project espressif/idf:v4.3.1 /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig && idf.py build -DDEPTH=${{ matrix.depth }} -DBUILD_NUMBER=${BUILD_NUMBER}-${{ matrix.depth }} && zip -r build_output.zip build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
echo pulling custom docker image ${DOCKER_IMAGE_NAME}
docker pull ${DOCKER_IMAGE_NAME}
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project ${DOCKER_IMAGE_NAME} /bin/bash -c "pushd components/wifi-manager/webapp/ && npm rebuild node-sass && npm run-script build && popd"
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project ${DOCKER_IMAGE_NAME} /bin/bash -c "cp build-scripts/${TARGET_BUILD_NAME}-sdkconfig.defaults sdkconfig"
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project ${DOCKER_IMAGE_NAME} /bin/bash -c "idf.py build -DDEPTH=${{ matrix.depth }} -DBUILD_NUMBER=${BUILD_NUMBER}-${{ matrix.depth }}
docker run --env-file=${TARGET_BUILD_NAME}-env.txt -v $PWD:/project -w /project ${DOCKER_IMAGE_NAME} /bin/bash -c "zip -r build_output.zip build && zip build/${artifact_file_name} partitions*.csv build/*.bin build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flash_project_args build/size_*.txt"
# - name: Build Mock firmware
# run: |
# mkdir -p build

View File

@@ -30,10 +30,10 @@ set(PROJECT_VER $ENV{PROJECT_VER})
#target_compile_definitions(__idf_services PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
#target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
#target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
#target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
#target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
target_compile_definitions(__idf_esp_eth PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_WARN)
target_compile_definitions(__idf_driver PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_WARN)
target_compile_definitions(__idf_wifi-manager PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_WARN)
target_compile_definitions(__idf_esp_wifi PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_WARN)
#target_compile_definitions(__idf_platform_console PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
#target_compile_definitions(__idf_app_recovery PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_INFO)
@@ -42,7 +42,7 @@ set(PROJECT_VER $ENV{PROJECT_VER})
# target_compile_definitions(__idf_esp_netif PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
# target_compile_definitions(__idf_freertos PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
# target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
target_compile_definitions(__idf_bt PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_ERROR)
# target_compile_definitions(__idf_mdns PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
# target_compile_definitions(__idf_tcpip_adapter PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)
# target_compile_definitions(__idf_tcp_transport PRIVATE -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG)

View File

@@ -1,33 +1,92 @@
FROM ubuntu:18.04
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y git wget libncurses-dev flex bison gperf \
python python-pip python-setuptools python-serial python-click \
python-cryptography python-future python-pyparsing \
python-pyelftools cmake ninja-build ccache libusb-1.0
RUN mkdir /workspace
WORKDIR /workspace
ARG DEBIAN_FRONTEND=noninteractive
# Download and checkout known good esp-idf commit
RUN git clone --recursive https://github.com/espressif/esp-idf.git esp-idf
RUN cd esp-idf && git checkout 4dac7c7df885adaa86a5c79f2adeaf8d68667349
RUN git clone https://github.com/sle118/squeezelite-esp32.git
COPY components/wifi-manager/webapp/package.json /opt
# Download GCC 5.2.0
RUN wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
RUN tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
RUN rm xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
# We need libpython2.7 due to GDB tools
RUN : \
&& apt-get update \
&& apt-get install -y \
apt-utils \
bison \
ca-certificates \
ccache \
check \
curl \
flex \
git \
gperf \
lcov \
libffi-dev \
libncurses-dev \
libpython2.7 \
libusb-1.0-0-dev \
make \
ninja-build \
python3 \
python3-pip \
unzip \
wget \
xz-utils \
zip \
npm \
nodejs \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \
&& python -m pip install --upgrade \
pip \
virtualenv \
&& cd /opt \
&& npm install -g \
&& :
RUN rm -r /workspace/squeezelite-esp32
RUN mkdir /workspace/squeezelite-esp32
# To build the image for a branch or a tag of IDF, pass --build-arg IDF_CLONE_BRANCH_OR_TAG=name.
# To build the image with a specific commit ID of IDF, pass --build-arg IDF_CHECKOUT_REF=commit-id.
# It is possibe to combine both, e.g.:
# IDF_CLONE_BRANCH_OR_TAG=release/vX.Y
# IDF_CHECKOUT_REF=<some commit on release/vX.Y branch>.
# The following commit contains the ldgen fix: eab738c79e063b3d6f4c345ea5e1d4f8caef725b
# to build an image using that commit: docker build . --build-arg IDF_CHECKOUT_REF=eab738c79e063b3d6f4c345ea5e1d4f8caef725b -t sle118/squeezelite-esp32-idfv4-master
# docker build . --build-arg IDF_CHECKOUT_REF=8bf14a9238329954c7c5062eeeda569529aedf75 -t sle118/squeezelite-esp32-idfv4-master
# To run the image interactive (windows): docker run --rm -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv4-master
# to build the web app inside of the interactive session
# pushd components/wifi-manager/webapp/ && npm rebuild node-sass && npm run-script build && popd
# Setup PATH to use esp-idf and gcc-5.2.0
RUN touch /root/.bashrc && \
echo export PATH="\$PATH:/workspace/xtensa-esp32-elf/bin" >> /root/.bashrc && \
echo export IDF_PATH=/workspace/esp-idf >> /root/.bashrc
ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
ARG IDF_CLONE_BRANCH_OR_TAG=master
ARG IDF_CHECKOUT_REF=eab738c79e063b3d6f4c345ea5e1d4f8caef725b
# OPTIONAL: Install vim for text editing in Bash
RUN apt-get update && apt-get install -y vim
ENV IDF_PATH=/opt/esp/idf
ENV IDF_TOOLS_PATH=/opt/esp
WORKDIR /workspace/squeezelite-esp32
CMD ["bash"]
RUN echo IDF_CHECKOUT_REF=$IDF_CHECKOUT_REF IDF_CLONE_BRANCH_OR_TAG=$IDF_CLONE_BRANCH_OR_TAG && \
git clone --recursive \
${IDF_CLONE_BRANCH_OR_TAG:+-b $IDF_CLONE_BRANCH_OR_TAG} \
$IDF_CLONE_URL $IDF_PATH && \
if [ -n "$IDF_CHECKOUT_REF" ]; then \
cd $IDF_PATH && \
git checkout $IDF_CHECKOUT_REF && \
git submodule update --init --recursive; \
fi
COPY docker/patches $IDF_PATH
# Install all the required tools
RUN : \
&& update-ca-certificates --fresh \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install required \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install cmake \
&& $IDF_PATH/tools/idf_tools.py --non-interactive install-python-env \
&& rm -rf $IDF_TOOLS_PATH/dist \
&& :
# Ccache is installed, enable it by default
ENV IDF_CCACHE_ENABLE=1
COPY docker/entrypoint.sh /opt/esp/entrypoint.sh
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
CMD [ "/bin/bash" ]

View File

@@ -475,17 +475,19 @@ See squeezlite command line, but keys options are
# Building everything yourself
## Setting up ESP-IDF
### Docker
You can use docker to build squeezelite-esp32 (optional)
First you need to build the Docker container:
A simple alternative to building the project's binaries is to leverage the same docker image that is being used on the GitHub Actions to build our releases. The instructions below assume that you have cloned the squeezelite-esp32 code that you want to build locally and that you have opened a command line/bash session in the folder that contains the code.
Pull the most recent docker image for the environment:
```
docker build -t esp-idf .
docker pull sle118/squeezelite-esp32-idfv4-master
```
Then you need to run the container:
Then run the container interactively :
```
docker run -i -t -v `pwd`:/workspace/squeezelite-esp32 esp-idf
for windows:
docker run -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv4-master
for linux:
docker run -it -v `pwd`:/workspace/squeezelite-esp32 sle118/squeezelite-esp32-idfv4-master
```
The above command will mount this repo into the docker container and start a bash terminal
for you to then follow the below build steps
The above command will mount this repo into the docker container and start a bash terminal. From there, simply run idf.py build to build, etc. Note that at the time of writing these lines, flashing is not possible for docker running under windows https://github.com/docker/for-win/issues/1018.
### Manual Install of ESP-IDF
You can install IDF manually on Linux or Windows (using the Subsystem for Linux) following the instructions at: https://www.instructables.com/id/ESP32-Development-on-Windows-Subsystem-for-Linux/ or see here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/windows-setup.html for a direct install.

View File

@@ -48,9 +48,13 @@ EXT_RAM_ATTR static struct {
struct arg_end *end;
} name_args;
EXT_RAM_ATTR static struct {
#if CONFIG_CSPOT_SINK
struct arg_lit *cspot;
#endif
struct arg_lit *btspeaker;
struct arg_lit *airplay;
struct arg_str *telnet;
#if WITH_TASKS_INFO
struct arg_lit *stats;
#endif
@@ -687,6 +691,9 @@ static int do_set_services(int argc, char **argv)
nerrors += enable_disable(f,"enable_airplay",set_services_args.airplay);
nerrors += enable_disable(f,"enable_bt_sink",set_services_args.btspeaker);
#if CONFIG_CSPOT_SINK
nerrors += enable_disable(f,"enable_cspot",set_services_args.cspot);
#endif
if(set_services_args.telnet->count>0){
if(strcasecmp(set_services_args.telnet->sval[0],"Disabled") == 0){
@@ -737,37 +744,37 @@ cJSON * configure_wifi_cb(){
cJSON * set_services_cb(){
cJSON * values = cJSON_CreateObject();
char * p=NULL;
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_bt_sink")) != NULL) {
cJSON_AddBoolToObject(values,"BT_Speaker",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_airplay")) != NULL) {
cJSON_AddBoolToObject(values,"AirPlay",strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
console_set_bool_parameter(values,"enable_bt_sink",set_services_args.btspeaker);
console_set_bool_parameter(values,"enable_airplay",set_services_args.airplay);
#if CONFIG_CSPOT_SINK
console_set_bool_parameter(values,"enable_cspot",set_services_args.cspot);
#endif
#if WITH_TASKS_INFO
console_set_bool_parameter(values,"stats",set_services_args.stats);
#endif
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
if(strcasestr("YX",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet Only");
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet Only");
}
else if(strcasestr("D",p)!=NULL){
cJSON_AddStringToObject(values,"telnet","Telnet and Serial");
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet and Serial");
}
else {
cJSON_AddStringToObject(values,"telnet","Disabled");
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Disabled");
}
FREE_AND_NULL(p);
}
#if WITH_TASKS_INFO
if((p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0))!=NULL){
cJSON_AddBoolToObject(values,"stats",(*p == '1' || *p == 'Y' || *p == 'y')) ;
}
#endif
return values;
}
static void register_set_services(){
set_services_args.airplay = arg_lit0(NULL, "AirPlay", "AirPlay");
#if CONFIG_CSPOT_SINK
set_services_args.cspot = arg_lit0(NULL, "cspot", "Spotify (cspot)");
#endif
set_services_args.btspeaker = arg_lit0(NULL, "BT_Speaker", "Bluetooth Speaker");
set_services_args.telnet= arg_str0("t", "telnet","Disabled|Telnet Only|Telnet and Serial","Telnet server. Use only for troubleshooting");
#if WITH_TASKS_INFO

View File

@@ -83,7 +83,17 @@ cJSON * get_cmd_list(){
}
return list;
}
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
char * p=NULL;
if(!root) {
ESP_LOGE(TAG,"Invalid json parameter. Cannot set %s from %s",arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary,nvs_name);
return;
}
if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
cJSON_AddBoolToObject(root,arg->hdr.longopts,strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
FREE_AND_NULL(p);
}
}
struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
if(!argtable) return NULL;
struct arg_hdr * *table = (struct arg_hdr * *)argtable;

View File

@@ -22,6 +22,7 @@ typedef cJSON * parm_values_fn_t(void);
esp_err_t cmd_to_json(const esp_console_cmd_t *cmd);
esp_err_t cmd_to_json_with_cb(const esp_console_cmd_t *cmd, parm_values_fn_t parm_values_fn);
int arg_parse_msg(int argc, char **argv, struct arg_hdr ** args);
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg);
cJSON * get_cmd_list();
#ifdef __cplusplus
}

View File

@@ -36,6 +36,17 @@
}
]
},
{
"name": "Squeezeamp",
"config": [
{"batt_config": "channel=7,scale=20.24"},
{"dac_config": "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"},
{"dac_controlset": ""},
{"set_GPIO": "12=green,13=red,34=jack,2=spkfault"},
{"spdif_config": "bck=33,ws=25,do=15"}
]
},
{
"name": "T-WATCH2020 by LilyGo",
"config": [

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS operator.cpp tools.c
idf_component_register(SRCS operator.cpp tools.c trace.c
REQUIRES esp_common pthread
INCLUDE_DIRS .
)

View File

@@ -32,6 +32,15 @@ extern "C" {
#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e)
#ifdef ENABLE_MEMTRACE
void memtrace_print_delta(const char * msg, const char * tag, const char * function);
#define MEMTRACE_PRINT_DELTA() memtrace_print_delta(NULL,TAG,__FUNCTION__);
#define MEMTRACE_PRINT_DELTA_MESSAGE(x) memtrace_print_delta(x,TAG,__FUNCTION__);
#else
#define MEMTRACE_PRINT_DELTA()
#define MEMTRACE_PRINT_DELTA_MESSAGE(x) ESP_LOGD(TAG,"%s",x);
#endif
#ifndef FREE_AND_NULL
#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; }
#endif

View File

@@ -10,6 +10,7 @@
#include "freertos/task.h"
#include "esp_event.h"
#include "trace.h"
#include "tools.h"
static const char TAG[] = "TRACE";

View File

@@ -165,7 +165,9 @@ static state_machine_result_t handle_global_event(state_machine_t* state_machine
switch (net_sm->Machine.Event) {
case EN_UPDATE_STATUS:
// handle the event, but don't swicth
MEMTRACE_PRINT_DELTA_MESSAGE("handle EN_UPDATE_STATUS - start");
network_status_update_basic_info();
MEMTRACE_PRINT_DELTA_MESSAGE("handle EN_UPDATE_STATUS - end");
return EVENT_HANDLED;
/* code */
break;

View File

@@ -1 +1 @@
[{"/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js":"1"},{"size":59815,"mtime":1618633783112,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"15w6qa4","/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js",[]]
[{"/project/components/wifi-manager/webapp/src/js/custom.js":"1"},{"size":61745,"mtime":1638897104015,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"mubdvi","/project/components/wifi-manager/webapp/src/js/custom.js",[]]

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.0e064e.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.0e064e.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.0e064e.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.1be2f3.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.1be2f3.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.1be2f3.bundle.js.gz BINARY)

View File

@@ -4,31 +4,31 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_0e064e_bundle_js_gz_start[] asm("_binary_index_0e064e_bundle_js_gz_start");
extern const uint8_t _index_0e064e_bundle_js_gz_end[] asm("_binary_index_0e064e_bundle_js_gz_end");
extern const uint8_t _node_modules_0e064e_bundle_js_gz_start[] asm("_binary_node_modules_0e064e_bundle_js_gz_start");
extern const uint8_t _node_modules_0e064e_bundle_js_gz_end[] asm("_binary_node_modules_0e064e_bundle_js_gz_end");
extern const uint8_t _runtime_0e064e_bundle_js_gz_start[] asm("_binary_runtime_0e064e_bundle_js_gz_start");
extern const uint8_t _runtime_0e064e_bundle_js_gz_end[] asm("_binary_runtime_0e064e_bundle_js_gz_end");
extern const uint8_t _index_1be2f3_bundle_js_gz_start[] asm("_binary_index_1be2f3_bundle_js_gz_start");
extern const uint8_t _index_1be2f3_bundle_js_gz_end[] asm("_binary_index_1be2f3_bundle_js_gz_end");
extern const uint8_t _node_modules_1be2f3_bundle_js_gz_start[] asm("_binary_node_modules_1be2f3_bundle_js_gz_start");
extern const uint8_t _node_modules_1be2f3_bundle_js_gz_end[] asm("_binary_node_modules_1be2f3_bundle_js_gz_end");
extern const uint8_t _runtime_1be2f3_bundle_js_gz_start[] asm("_binary_runtime_1be2f3_bundle_js_gz_start");
extern const uint8_t _runtime_1be2f3_bundle_js_gz_end[] asm("_binary_runtime_1be2f3_bundle_js_gz_end");
const char * resource_lookups[] = {
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.0e064e.bundle.js.gz",
"/js/node-modules.0e064e.bundle.js.gz",
"/js/runtime.0e064e.bundle.js.gz",
"/js/index.1be2f3.bundle.js.gz",
"/js/node-modules.1be2f3.bundle.js.gz",
"/js/runtime.1be2f3.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_favicon_32x32_png_start,
_index_html_gz_start,
_index_0e064e_bundle_js_gz_start,
_node_modules_0e064e_bundle_js_gz_start,
_runtime_0e064e_bundle_js_gz_start
_index_1be2f3_bundle_js_gz_start,
_node_modules_1be2f3_bundle_js_gz_start,
_runtime_1be2f3_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_favicon_32x32_png_end,
_index_html_gz_end,
_index_0e064e_bundle_js_gz_end,
_node_modules_0e064e_bundle_js_gz_end,
_runtime_0e064e_bundle_js_gz_end
_index_1be2f3_bundle_js_gz_end,
_node_modules_1be2f3_bundle_js_gz_end,
_runtime_1be2f3_bundle_js_gz_end
};

View File

@@ -1,40 +1,40 @@
/***********************************
webpack_headers
Hash: 0e064eadc7c8b7881633
Hash: 1be2f373b8e586c66ebe
Version: webpack 4.46.0
Time: 9582ms
Built at: 2021-07-09 11 h 14 min 41 s
Time: 59080ms
Built at: 12/28/2021 5:04:54 PM
Asset Size Chunks Chunk Names
./js/index.0e064e.bundle.js 232 KiB 0 [emitted] [immutable] index
./js/index.0e064e.bundle.js.br 32.7 KiB [emitted]
./js/index.0e064e.bundle.js.gz 42 KiB [emitted]
./js/node-modules.0e064e.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
./js/node-modules.0e064e.bundle.js.br 76.3 KiB [emitted]
./js/node-modules.0e064e.bundle.js.gz 88.7 KiB [emitted]
./js/runtime.0e064e.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
./js/runtime.0e064e.bundle.js.br 644 bytes [emitted]
./js/runtime.0e064e.bundle.js.gz 722 bytes [emitted]
./js/index.1be2f3.bundle.js 232 KiB 0 [emitted] [immutable] index
./js/index.1be2f3.bundle.js.br 32.6 KiB [emitted]
./js/index.1be2f3.bundle.js.gz 42 KiB [emitted]
./js/node-modules.1be2f3.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
./js/node-modules.1be2f3.bundle.js.br 76.3 KiB [emitted]
./js/node-modules.1be2f3.bundle.js.gz 88.7 KiB [emitted]
./js/runtime.1be2f3.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
./js/runtime.1be2f3.bundle.js.br 644 bytes [emitted]
./js/runtime.1be2f3.bundle.js.gz 722 bytes [emitted]
favicon-32x32.png 634 bytes [emitted]
index.html 21.7 KiB [emitted]
index.html.br 4.74 KiB [emitted]
index.html.br 4.76 KiB [emitted]
index.html.gz 5.75 KiB [emitted]
sprite.svg 4.4 KiB [emitted]
sprite.svg.br 898 bytes [emitted]
Entrypoint index [big] = ./js/runtime.0e064e.bundle.js ./js/node-modules.0e064e.bundle.js ./js/index.0e064e.bundle.js
sprite.svg.br 903 bytes [emitted]
Entrypoint index [big] = ./js/runtime.1be2f3.bundle.js ./js/node-modules.1be2f3.bundle.js ./js/index.1be2f3.bundle.js
[6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
[11] ./src/sass/main.scss 1.55 KiB {0} [built]
[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 340 bytes {1} [built]
[17] ./node_modules/remixicon/icons/Device/signal-wifi-3-fill.svg 344 bytes {1} [built]
[18] ./node_modules/remixicon/icons/Device/signal-wifi-2-fill.svg 344 bytes {1} [built]
[19] ./node_modules/remixicon/icons/Device/signal-wifi-1-fill.svg 344 bytes {1} [built]
[20] ./node_modules/remixicon/icons/Device/signal-wifi-line.svg 340 bytes {1} [built]
[21] ./node_modules/remixicon/icons/Device/battery-line.svg 332 bytes {1} [built]
[22] ./node_modules/remixicon/icons/Device/battery-low-line.svg 340 bytes {1} [built]
[23] ./node_modules/remixicon/icons/Device/battery-fill.svg 332 bytes {1} [built]
[24] ./node_modules/remixicon/icons/Media/headphone-fill.svg 335 bytes {1} [built]
[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 346 bytes {1} [built]
[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 336 bytes {1} [built]
[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 352 bytes {1} [built]
[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 286 bytes {1} [built]
[17] ./node_modules/remixicon/icons/Device/signal-wifi-3-fill.svg 290 bytes {1} [built]
[18] ./node_modules/remixicon/icons/Device/signal-wifi-2-fill.svg 290 bytes {1} [built]
[19] ./node_modules/remixicon/icons/Device/signal-wifi-1-fill.svg 290 bytes {1} [built]
[20] ./node_modules/remixicon/icons/Device/signal-wifi-line.svg 286 bytes {1} [built]
[21] ./node_modules/remixicon/icons/Device/battery-line.svg 278 bytes {1} [built]
[22] ./node_modules/remixicon/icons/Device/battery-low-line.svg 286 bytes {1} [built]
[23] ./node_modules/remixicon/icons/Device/battery-fill.svg 278 bytes {1} [built]
[24] ./node_modules/remixicon/icons/Media/headphone-fill.svg 281 bytes {1} [built]
[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 292 bytes {1} [built]
[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 282 bytes {1} [built]
[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 298 bytes {1} [built]
[38] ./src/index.ts + 1 modules 62.6 KiB {0} [built]
| ./src/index.ts 1.4 KiB [built]
| ./src/js/custom.js 61 KiB [built]
@@ -43,14 +43,14 @@ Entrypoint index [big] = ./js/runtime.0e064e.bundle.js ./js/node-modules.0e064e.
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
./js/node-modules.0e064e.bundle.js (266 KiB)
./js/node-modules.1be2f3.bundle.js (266 KiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
index (499 KiB)
./js/runtime.0e064e.bundle.js
./js/node-modules.0e064e.bundle.js
./js/index.0e064e.bundle.js
./js/runtime.1be2f3.bundle.js
./js/node-modules.1be2f3.bundle.js
./js/index.1be2f3.bundle.js
WARNING in webpack performance recommendations:

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg"><defs><symbol viewBox="0 0 24 24" id="battery-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm18 4h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="battery-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 7v10h14V7H4zM3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm18 4h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="battery-low-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 7v10h14V7H4zM3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm2 3h4v8H5V8zm16 1h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="bluetooth-connect-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M14.341 12.03l4.343 4.343-5.656 5.656h-2v-6.686l-4.364 4.364-1.415-1.414 5.779-5.778v-.97l-5.779-5.78 1.415-1.414 4.364 4.364V2.029h2l5.656 5.657-4.343 4.343zm-1.313 1.514v5.657l2.828-2.828-2.828-2.829zm0-3.03l2.828-2.828-2.828-2.828v5.657zM19.5 13.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm-13 0a1.5 1.5 0 110-3 1.5 1.5 0 010 3z"/></symbol><symbol viewBox="0 0 24 24" id="bluetooth-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M14.341 12.03l4.343 4.343-5.656 5.656h-2v-6.686l-4.364 4.364-1.415-1.414 5.779-5.778v-.97l-5.779-5.78 1.415-1.414 4.364 4.364V2.029h2l5.656 5.657-4.343 4.343zm-1.313 1.514v5.657l2.828-2.828-2.828-2.829zm0-3.03l2.828-2.828-2.828-2.828v5.657z"/></symbol><symbol viewBox="0 0 24 24" id="device-recover-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M19 2a1 1 0 011 1v18a1 1 0 01-1 1H5a1 1 0 01-1-1V3a1 1 0 011-1h14zm-7 5a5 5 0 10.955 9.909L12 15a3 3 0 010-6c1.598 0 3 1.34 3 3h-2.5l2.128 4.254A5 5 0 0012 7z"/></symbol><symbol viewBox="0 0 24 24" id="headphone-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 12h3a2 2 0 012 2v5a2 2 0 01-2 2H4a2 2 0 01-2-2v-7C2 6.477 6.477 2 12 2s10 4.477 10 10v7a2 2 0 01-2 2h-3a2 2 0 01-2-2v-5a2 2 0 012-2h3a8 8 0 10-16 0z"/></symbol><symbol viewBox="0 0 24 24" id="lock-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M19 10h1a1 1 0 011 1v10a1 1 0 01-1 1H4a1 1 0 01-1-1V11a1 1 0 011-1h1V9a7 7 0 1114 0v1zm-2 0V9A5 5 0 007 9v1h10zm-6 4v4h2v-4h-2z"/></symbol><symbol viewBox="0 0 24 24" id="lock-unlock-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 10h13a1 1 0 011 1v10a1 1 0 01-1 1H4a1 1 0 01-1-1V11a1 1 0 011-1h1V9a7 7 0 0113.262-3.131l-1.789.894A5 5 0 007 9v1zm3 5v2h4v-2h-4z"/></symbol><symbol viewBox="0 0 24 24" id="pause-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM9 9v6h2V9H9zm4 0v6h2V9h-2z"/></symbol><symbol viewBox="0 0 24 24" id="play-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM10.622 8.415a.4.4 0 00-.622.332v6.506a.4.4 0 00.622.332l4.879-3.252a.4.4 0 000-.666l-4.88-3.252z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-1-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l5.108 6.324A7.965 7.965 0 0112 13c1.181 0 2.303.256 3.312.716L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-2-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l3.178 3.935A10.953 10.953 0 0112 10c1.898 0 3.683.48 5.241 1.327L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-3-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l1.904 2.357C7.4 8.637 9.625 8 12 8s4.6.637 6.516 1.749L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392L12 17.817 20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="stop-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM9 9v6h6V9H9z"/></symbol></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg"><defs><symbol viewBox="0 0 24 24" id="battery-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 7v10h14V7H4zM3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm18 4h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="lock-unlock-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 10h13a1 1 0 011 1v10a1 1 0 01-1 1H4a1 1 0 01-1-1V11a1 1 0 011-1h1V9a7 7 0 0113.262-3.131l-1.789.894A5 5 0 007 9v1zm3 5v2h4v-2h-4z"/></symbol><symbol viewBox="0 0 24 24" id="battery-low-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 7v10h14V7H4zM3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm2 3h4v8H5V8zm16 1h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="bluetooth-connect-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M14.341 12.03l4.343 4.343-5.656 5.656h-2v-6.686l-4.364 4.364-1.415-1.414 5.779-5.778v-.97l-5.779-5.78 1.415-1.414 4.364 4.364V2.029h2l5.656 5.657-4.343 4.343zm-1.313 1.514v5.657l2.828-2.828-2.828-2.829zm0-3.03l2.828-2.828-2.828-2.828v5.657zM19.5 13.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm-13 0a1.5 1.5 0 110-3 1.5 1.5 0 010 3z"/></symbol><symbol viewBox="0 0 24 24" id="bluetooth-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M14.341 12.03l4.343 4.343-5.656 5.656h-2v-6.686l-4.364 4.364-1.415-1.414 5.779-5.778v-.97l-5.779-5.78 1.415-1.414 4.364 4.364V2.029h2l5.656 5.657-4.343 4.343zm-1.313 1.514v5.657l2.828-2.828-2.828-2.829zm0-3.03l2.828-2.828-2.828-2.828v5.657z"/></symbol><symbol viewBox="0 0 24 24" id="device-recover-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M19 2a1 1 0 011 1v18a1 1 0 01-1 1H5a1 1 0 01-1-1V3a1 1 0 011-1h14zm-7 5a5 5 0 10.955 9.909L12 15a3 3 0 010-6c1.598 0 3 1.34 3 3h-2.5l2.128 4.254A5 5 0 0012 7z"/></symbol><symbol viewBox="0 0 24 24" id="headphone-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 12h3a2 2 0 012 2v5a2 2 0 01-2 2H4a2 2 0 01-2-2v-7C2 6.477 6.477 2 12 2s10 4.477 10 10v7a2 2 0 01-2 2h-3a2 2 0 01-2-2v-5a2 2 0 012-2h3a8 8 0 10-16 0z"/></symbol><symbol viewBox="0 0 24 24" id="lock-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M19 10h1a1 1 0 011 1v10a1 1 0 01-1 1H4a1 1 0 01-1-1V11a1 1 0 011-1h1V9a7 7 0 1114 0v1zm-2 0V9A5 5 0 007 9v1h10zm-6 4v4h2v-4h-2z"/></symbol><symbol viewBox="0 0 24 24" id="battery-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 5h16a1 1 0 011 1v12a1 1 0 01-1 1H3a1 1 0 01-1-1V6a1 1 0 011-1zm18 4h2v6h-2V9z"/></symbol><symbol viewBox="0 0 24 24" id="pause-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM9 9v6h2V9H9zm4 0v6h2V9h-2z"/></symbol><symbol viewBox="0 0 24 24" id="play-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM10.622 8.415a.4.4 0 00-.622.332v6.506a.4.4 0 00.622.332l4.879-3.252a.4.4 0 000-.666l-4.88-3.252z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-1-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l5.108 6.324A7.965 7.965 0 0112 13c1.181 0 2.303.256 3.312.716L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-2-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l3.178 3.935A10.953 10.953 0 0112 10c1.898 0 3.683.48 5.241 1.327L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-3-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392l1.904 2.357C7.4 8.637 9.625 8 12 8s4.6.637 6.516 1.749L20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3z"/></symbol><symbol viewBox="0 0 24 24" id="signal-wifi-line"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 3c4.284 0 8.22 1.497 11.31 3.996L12 21 .69 6.997A17.917 17.917 0 0112 3zm0 2c-3.028 0-5.923.842-8.42 2.392L12 17.817 20.42 7.39A15.928 15.928 0 0012 5z"/></symbol><symbol viewBox="0 0 24 24" id="stop-circle-fill"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM9 9v6h6V9H9z"/></symbol></defs></svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -181,8 +181,8 @@ module.exports = merge(common, {
});
console.log('Post build completed.');
}),
new BundleAnalyzerPlugin()
})
//, new BundleAnalyzerPlugin()
]
});

6
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
. $IDF_PATH/export.sh
exec "$@"

14
docker/hooks/build Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# This file gets executed to build the image on the Docker Hub.
# See https://docs.docker.com/docker-hub/builds/advanced/#build-hook-examples for details.
set -euo pipefail
echo "Building for branch ${SOURCE_BRANCH}, commit ${SOURCE_COMMIT}"
docker build \
--build-arg IDF_CLONE_BRANCH_OR_TAG=${SOURCE_BRANCH} \
--build-arg IDF_CHECKOUT_REF=${SOURCE_COMMIT} \
-f $DOCKERFILE_PATH \
-t $IMAGE_NAME .

View File

@@ -0,0 +1,607 @@
#
# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
import abc
import os
import re
from collections import namedtuple
from enum import Enum
from entity import Entity
from pyparsing import (Combine, Forward, Group, Keyword, Literal, OneOrMore, Optional, Or, ParseFatalException,
Suppress, Word, ZeroOrMore, alphanums, alphas, delimitedList, indentedBlock, nums,
originalTextFor, restOfLine)
from sdkconfig import SDKConfig
class FragmentFile():
"""
Processes a fragment file and stores all parsed fragments. For
more information on how this class interacts with classes for the different fragment types,
see description of Fragment.
"""
def __init__(self, fragment_file, sdkconfig):
try:
fragment_file = open(fragment_file, 'r')
except TypeError:
pass
path = os.path.realpath(fragment_file.name)
indent_stack = [1]
class parse_ctx:
fragment = None # current fragment
key = '' # current key
keys = list() # list of keys parsed
key_grammar = None # current key grammar
@staticmethod
def reset():
parse_ctx.fragment_instance = None
parse_ctx.key = ''
parse_ctx.keys = list()
parse_ctx.key_grammar = None
def fragment_type_parse_action(toks):
parse_ctx.reset()
parse_ctx.fragment = FRAGMENT_TYPES[toks[0]]() # create instance of the fragment
return None
def expand_conditionals(toks, stmts):
try:
stmt = toks['value']
stmts.append(stmt)
except KeyError:
try:
conditions = toks['conditional']
for condition in conditions:
try:
_toks = condition[1]
_cond = condition[0]
if sdkconfig.evaluate_expression(_cond):
expand_conditionals(_toks, stmts)
break
except IndexError:
expand_conditionals(condition[0], stmts)
except KeyError:
for tok in toks:
expand_conditionals(tok, stmts)
def key_body_parsed(pstr, loc, toks):
stmts = list()
expand_conditionals(toks, stmts)
if parse_ctx.key_grammar.min and len(stmts) < parse_ctx.key_grammar.min:
raise ParseFatalException(pstr, loc, "fragment requires at least %d values for key '%s'" %
(parse_ctx.key_grammar.min, parse_ctx.key))
if parse_ctx.key_grammar.max and len(stmts) > parse_ctx.key_grammar.max:
raise ParseFatalException(pstr, loc, "fragment requires at most %d values for key '%s'" %
(parse_ctx.key_grammar.max, parse_ctx.key))
try:
parse_ctx.fragment.set_key_value(parse_ctx.key, stmts)
except Exception as e:
raise ParseFatalException(pstr, loc, "unable to add key '%s'; %s" % (parse_ctx.key, str(e)))
return None
key = Word(alphanums + '_') + Suppress(':')
key_stmt = Forward()
condition_block = indentedBlock(key_stmt, indent_stack)
key_stmts = OneOrMore(condition_block)
key_body = Suppress(key) + key_stmts
key_body.setParseAction(key_body_parsed)
condition = originalTextFor(SDKConfig.get_expression_grammar()).setResultsName('condition')
if_condition = Group(Suppress('if') + condition + Suppress(':') + condition_block)
elif_condition = Group(Suppress('elif') + condition + Suppress(':') + condition_block)
else_condition = Group(Suppress('else') + Suppress(':') + condition_block)
conditional = (if_condition + Optional(OneOrMore(elif_condition)) + Optional(else_condition)).setResultsName('conditional')
def key_parse_action(pstr, loc, toks):
key = toks[0]
if key in parse_ctx.keys:
raise ParseFatalException(pstr, loc, "duplicate key '%s' value definition" % parse_ctx.key)
parse_ctx.key = key
parse_ctx.keys.append(key)
try:
parse_ctx.key_grammar = parse_ctx.fragment.get_key_grammars()[key]
key_grammar = parse_ctx.key_grammar.grammar
except KeyError:
raise ParseFatalException(pstr, loc, "key '%s' is not supported by fragment" % key)
except Exception as e:
raise ParseFatalException(pstr, loc, "unable to parse key '%s'; %s" % (key, str(e)))
key_stmt << (conditional | Group(key_grammar).setResultsName('value'))
return None
def name_parse_action(pstr, loc, toks):
parse_ctx.fragment.name = toks[0]
key.setParseAction(key_parse_action)
ftype = Word(alphas).setParseAction(fragment_type_parse_action)
fid = Suppress(':') + Word(alphanums + '_.').setResultsName('name')
fid.setParseAction(name_parse_action)
header = Suppress('[') + ftype + fid + Suppress(']')
def fragment_parse_action(pstr, loc, toks):
key_grammars = parse_ctx.fragment.get_key_grammars()
required_keys = set([k for (k,v) in key_grammars.items() if v.required])
present_keys = required_keys.intersection(set(parse_ctx.keys))
if present_keys != required_keys:
raise ParseFatalException(pstr, loc, 'required keys %s for fragment not found' %
list(required_keys - present_keys))
return parse_ctx.fragment
fragment_stmt = Forward()
fragment_block = indentedBlock(fragment_stmt, indent_stack)
fragment_if_condition = Group(Suppress('if') + condition + Suppress(':') + fragment_block)
fragment_elif_condition = Group(Suppress('elif') + condition + Suppress(':') + fragment_block)
fragment_else_condition = Group(Suppress('else') + Suppress(':') + fragment_block)
fragment_conditional = (fragment_if_condition + Optional(OneOrMore(fragment_elif_condition)) +
Optional(fragment_else_condition)).setResultsName('conditional')
fragment = (header + OneOrMore(indentedBlock(key_body, indent_stack, False))).setResultsName('value')
fragment.setParseAction(fragment_parse_action)
fragment.ignore('#' + restOfLine)
deprecated_mapping = DeprecatedMapping.get_fragment_grammar(sdkconfig, fragment_file.name).setResultsName('value')
fragment_stmt << (Group(deprecated_mapping) | Group(fragment) | Group(fragment_conditional))
def fragment_stmt_parsed(pstr, loc, toks):
stmts = list()
expand_conditionals(toks, stmts)
return stmts
parser = ZeroOrMore(fragment_stmt)
parser.setParseAction(fragment_stmt_parsed)
self.fragments = parser.parseFile(fragment_file, parseAll=True)
for fragment in self.fragments:
fragment.path = path
class Fragment():
"""
Base class for a fragment that can be parsed from a fragment file. All fragments
share the common grammar:
[type:name]
key1:value1
key2:value2
...
Supporting a new fragment type means deriving a concrete class which specifies
key-value pairs that the fragment supports and what to do with the parsed key-value pairs.
The new fragment must also be appended to FRAGMENT_TYPES, specifying the
keyword for the type and the derived class.
The key of the key-value pair is a simple keyword string. Other parameters
that describe the key-value pair is specified in Fragment.KeyValue:
1. grammar - pyparsing grammar to parse the value of key-value pair
2. min - the minimum number of value in the key entry, None means no minimum
3. max - the maximum number of value in the key entry, None means no maximum
4. required - if the key-value pair is required in the fragment
Setting min=max=1 means that the key has a single value.
FragmentFile provides conditional expression evaluation, enforcing
the parameters for Fragment.Keyvalue.
"""
__metaclass__ = abc.ABCMeta
KeyValue = namedtuple('KeyValue', 'grammar min max required')
IDENTIFIER = Word(alphas + '_', alphanums + '_')
ENTITY = Word(alphanums + '.-_$+')
@abc.abstractmethod
def set_key_value(self, key, parse_results):
pass
@abc.abstractmethod
def get_key_grammars(self):
pass
class Sections(Fragment):
"""
Fragment which contains list of input sections.
[sections:<name>]
entries:
.section1
.section2
...
"""
# Unless quoted, symbol names start with a letter, underscore, or point
# and may include any letters, underscores, digits, points, and hyphens.
GNU_LD_SYMBOLS = Word(alphas + '_.', alphanums + '._-')
entries_grammar = Combine(GNU_LD_SYMBOLS + Optional('+'))
grammars = {
'entries': Fragment.KeyValue(entries_grammar.setResultsName('section'), 1, None, True)
}
"""
Utility function that returns a list of sections given a sections fragment entry,
with the '+' notation and symbol concatenation handled automatically.
"""
@staticmethod
def get_section_data_from_entry(sections_entry, symbol=None):
if not symbol:
sections = list()
sections.append(sections_entry.replace('+', ''))
sections.append(sections_entry.replace('+', '.*'))
return sections
else:
if sections_entry.endswith('+'):
section = sections_entry.replace('+', '.*')
expansion = section.replace('.*', '.' + symbol)
return (section, expansion)
else:
return (sections_entry, None)
def set_key_value(self, key, parse_results):
if key == 'entries':
self.entries = set()
for result in parse_results:
self.entries.add(result['section'])
def get_key_grammars(self):
return self.__class__.grammars
class Scheme(Fragment):
"""
Fragment which defines where the input sections defined in a Sections fragment
is going to end up, the target. The targets are markers in a linker script template
(see LinkerScript in linker_script.py).
[scheme:<name>]
entries:
sections1 -> target1
...
"""
grammars = {
'entries': Fragment.KeyValue(Fragment.IDENTIFIER.setResultsName('sections') + Suppress('->') +
Fragment.IDENTIFIER.setResultsName('target'), 1, None, True)
}
def set_key_value(self, key, parse_results):
if key == 'entries':
self.entries = set()
for result in parse_results:
self.entries.add((result['sections'], result['target']))
def get_key_grammars(self):
return self.__class__.grammars
class Mapping(Fragment):
"""
Fragment which attaches a scheme to entities (see Entity in entity.py), specifying where the input
sections of the entity will end up.
[mapping:<name>]
archive: lib1.a
entries:
obj1:symbol1 (scheme1); section1 -> target1 KEEP SURROUND(sym1) ...
obj2 (scheme2)
...
Ultimately, an `entity (scheme)` entry generates an
input section description (see https://sourceware.org/binutils/docs/ld/Input-Section.html)
in the output linker script. It is possible to attach 'flags' to the
`entity (scheme)` to generate different output commands or to
emit additional keywords in the generated input section description. The
input section description, as well as other output commands, is defined in
output_commands.py.
"""
class Flag():
PRE_POST = (Optional(Suppress(',') + Suppress('pre').setParseAction(lambda: True).setResultsName('pre')) +
Optional(Suppress(',') + Suppress('post').setParseAction(lambda: True).setResultsName('post')))
class Surround(Flag):
def __init__(self, symbol):
self.symbol = symbol
self.pre = True
self.post = True
@staticmethod
def get_grammar():
# SURROUND(symbol)
#
# '__symbol_start', '__symbol_end' is generated before and after
# the corresponding input section description, respectively.
grammar = (Keyword('SURROUND').suppress() +
Suppress('(') +
Fragment.IDENTIFIER.setResultsName('symbol') +
Suppress(')'))
grammar.setParseAction(lambda tok: Mapping.Surround(tok.symbol))
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Surround) and
self.symbol == other.symbol)
class Align(Flag):
def __init__(self, alignment, pre=True, post=False):
self.alignment = alignment
self.pre = pre
self.post = post
@staticmethod
def get_grammar():
# ALIGN(alignment, [, pre, post]).
#
# Generates alignment command before and/or after the corresponding
# input section description, depending whether pre, post or
# both are specified.
grammar = (Keyword('ALIGN').suppress() +
Suppress('(') +
Word(nums).setResultsName('alignment') +
Mapping.Flag.PRE_POST +
Suppress(')'))
def on_parse(tok):
alignment = int(tok.alignment)
if tok.pre == '' and tok.post == '':
res = Mapping.Align(alignment)
elif tok.pre != '' and tok.post == '':
res = Mapping.Align(alignment, tok.pre)
elif tok.pre == '' and tok.post != '':
res = Mapping.Align(alignment, False, tok.post)
else:
res = Mapping.Align(alignment, tok.pre, tok.post)
return res
grammar.setParseAction(on_parse)
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Align) and
self.alignment == other.alignment and
self.pre == other.pre and
self.post == other.post)
class Keep(Flag):
def __init__(self):
pass
@staticmethod
def get_grammar():
# KEEP()
#
# Surrounds input section description with KEEP command.
grammar = Keyword('KEEP()').setParseAction(Mapping.Keep)
return grammar
def __eq__(self, other):
return isinstance(other, Mapping.Keep)
class Sort(Flag):
class Type(Enum):
NAME = 0
ALIGNMENT = 1
INIT_PRIORITY = 2
def __init__(self, first, second=None):
self.first = first
self.second = second
@staticmethod
def get_grammar():
# SORT([sort_by_first, sort_by_second])
#
# where sort_by_first, sort_by_second = {name, alignment, init_priority}
#
# Emits SORT_BY_NAME, SORT_BY_ALIGNMENT or SORT_BY_INIT_PRIORITY
# depending on arguments. Nested sort follows linker script rules.
keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority')
grammar = (Keyword('SORT').suppress() + Suppress('(') +
keywords.setResultsName('first') +
Optional(Suppress(',') + keywords.setResultsName('second')) + Suppress(')'))
grammar.setParseAction(lambda tok: Mapping.Sort(tok.first, tok.second if tok.second != '' else None))
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Sort) and
self.first == other.first and
self.second == other.second)
def __init__(self):
Fragment.__init__(self)
self.entries = set()
# k = (obj, symbol, scheme)
# v = list((section, target), Mapping.Flag))
self.flags = dict()
self.deprecated = False
def set_key_value(self, key, parse_results):
if key == 'archive':
self.archive = parse_results[0]['archive']
elif key == 'entries':
for result in parse_results:
obj = None
symbol = None
scheme = None
obj = result['object']
try:
symbol = result['symbol']
except KeyError:
pass
scheme = result['scheme']
mapping = (obj, symbol, scheme)
self.entries.add(mapping)
try:
parsed_flags = result['sections_target_flags']
except KeyError:
parsed_flags = []
if parsed_flags:
entry_flags = []
for pf in parsed_flags:
entry_flags.append((pf.sections, pf.target, list(pf.flags)))
try:
existing_flags = self.flags[mapping]
except KeyError:
existing_flags = list()
self.flags[mapping] = existing_flags
existing_flags.extend(entry_flags)
def get_key_grammars(self):
# There are three possible patterns for mapping entries:
# obj:symbol (scheme)
# obj (scheme)
# * (scheme)
# Flags can be specified for section->target in the scheme specified, ex:
# obj (scheme); section->target SURROUND(symbol), section2->target2 ALIGN(4)
obj = Fragment.ENTITY.setResultsName('object')
symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol')
scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')')
# The flags are specified for section->target in the scheme specified
sections_target = Scheme.grammars['entries'].grammar
flag = Or([f.get_grammar() for f in [Mapping.Keep, Mapping.Align, Mapping.Surround, Mapping.Sort]])
section_target_flags = Group(sections_target + Group(OneOrMore(flag)).setResultsName('flags'))
pattern1 = obj + symbol
pattern2 = obj
pattern3 = Literal(Entity.ALL).setResultsName('object')
entry = ((pattern1 | pattern2 | pattern3) + scheme +
Optional(Suppress(';') + delimitedList(section_target_flags).setResultsName('sections_target_flags')))
grammars = {
'archive': Fragment.KeyValue(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True),
'entries': Fragment.KeyValue(entry, 0, None, True)
}
return grammars
class DeprecatedMapping():
"""
Mapping fragment with old grammar in versions older than ESP-IDF v4.0. Does not conform to
requirements of the Fragment class and thus is limited when it comes to conditional expression
evaluation.
"""
# Name of the default condition entry
DEFAULT_CONDITION = 'default'
@staticmethod
def get_fragment_grammar(sdkconfig, fragment_file):
# Match header [mapping]
header = Suppress('[') + Suppress('mapping') + Suppress(']')
# There are three possible patterns for mapping entries:
# obj:symbol (scheme)
# obj (scheme)
# * (scheme)
obj = Fragment.ENTITY.setResultsName('object')
symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol')
scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')')
pattern1 = Group(obj + symbol + scheme)
pattern2 = Group(obj + scheme)
pattern3 = Group(Literal(Entity.ALL).setResultsName('object') + scheme)
mapping_entry = pattern1 | pattern2 | pattern3
# To simplify parsing, classify groups of condition-mapping entry into two types: normal and default
# A normal grouping is one with a non-default condition. The default grouping is one which contains the
# default condition
mapping_entries = Group(ZeroOrMore(mapping_entry)).setResultsName('mappings')
normal_condition = Suppress(':') + originalTextFor(SDKConfig.get_expression_grammar())
default_condition = Optional(Suppress(':') + Literal(DeprecatedMapping.DEFAULT_CONDITION))
normal_group = Group(normal_condition.setResultsName('condition') + mapping_entries)
default_group = Group(default_condition + mapping_entries).setResultsName('default_group')
normal_groups = Group(ZeroOrMore(normal_group)).setResultsName('normal_groups')
# Any mapping fragment definition can have zero or more normal group and only one default group as a last entry.
archive = Suppress('archive') + Suppress(':') + Fragment.ENTITY.setResultsName('archive')
entries = Suppress('entries') + Suppress(':') + (normal_groups + default_group).setResultsName('entries')
mapping = Group(header + archive + entries)
mapping.ignore('#' + restOfLine)
def parsed_deprecated_mapping(pstr, loc, toks):
fragment = Mapping()
fragment.archive = toks[0].archive
fragment.name = re.sub(r'[^0-9a-zA-Z]+', '_', fragment.archive)
fragment.deprecated = True
fragment.entries = set()
condition_true = False
for entries in toks[0].entries[0]:
condition = next(iter(entries.condition.asList())).strip()
condition_val = sdkconfig.evaluate_expression(condition)
if condition_val:
for entry in entries[1]:
fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
condition_true = True
break
if not fragment.entries and not condition_true:
try:
entries = toks[0].entries[1][1]
except IndexError:
entries = toks[0].entries[1][0]
for entry in entries:
fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
if not fragment.entries:
fragment.entries.add(('*', None, 'default'))
dep_warning = str(ParseFatalException(pstr, loc,
'Warning: Deprecated old-style mapping fragment parsed in file %s.' % fragment_file))
print(dep_warning)
return fragment
mapping.setParseAction(parsed_deprecated_mapping)
return mapping
FRAGMENT_TYPES = {
'sections': Sections,
'scheme': Scheme,
'mapping': Mapping
}