From 6ecf5db54658f76312df600f97c4df6db5648735 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Mon, 2 Dec 2024 18:04:51 +0800 Subject: [PATCH] feat(repo): initialize with modules 'check', 'log' and 'utils' --- .codespellrc | 2 + .flake8 | 141 ++++++ ...heck_versions.sh => check_lib_versions.sh} | 46 +- .github/workflows/build_test.yml | 10 +- ...ck_versions.yml => check_lib_versions.yml} | 12 +- .github/workflows/pre-commit.yml | 2 +- .github/workflows/upload_component.yml | 20 + .pre-commit-config.yaml | 121 ++++- CHANGELOG.md | 4 +- CMakeLists.txt | 11 +- Kconfig | 111 +++++ README.md | 62 +-- check_copyright_config.yaml | 9 +- esp_utils_config.h | 102 +++++ idf_component.yml | 7 + library.properties | 14 +- src/check/esp_utils_check.h | 425 ++++++++++++++++++ src/esp_utils_config_internal.h | 66 +++ src/esp_utils_config_kconfig.h | 109 +++++ src/esp_utils_library.h | 12 + src/esp_utils_types.h | 28 ++ src/esp_utils_versions.h | 38 ++ src/log/esp_utils_log.c | 25 ++ src/log/esp_utils_log.h | 187 ++++++++ src/memory/esp_utils_memory.c | 42 ++ src/memory/esp_utils_memory.h | 98 ++++ test_apps/CMakeLists.txt | 6 + test_apps/main/CMakeLists.txt | 4 + test_apps/main/idf_component.yml | 7 + test_apps/main/test_app_main.cpp | 78 ++++ test_apps/main/test_on_c.c | 160 +++++++ test_apps/main/test_on_cpp.cpp | 175 ++++++++ test_apps/sdkconfig.ci.cxx_exceptions | 1 + test_apps/sdkconfig.defaults | 6 + tools/check_executables.py | 74 +++ tools/check_file_version.py | 125 ++++++ tools/executable-list.txt | 2 + tools/idf_ci_utils.py | 112 +++++ 38 files changed, 2356 insertions(+), 98 deletions(-) create mode 100644 .codespellrc create mode 100644 .flake8 rename .github/scripts/{check_versions.sh => check_lib_versions.sh} (89%) mode change 100644 => 100755 rename .github/workflows/{check_versions.yml => check_lib_versions.yml} (72%) create mode 100644 .github/workflows/upload_component.yml create mode 100644 Kconfig create mode 100644 esp_utils_config.h create mode 100644 idf_component.yml create mode 100644 src/check/esp_utils_check.h create mode 100644 src/esp_utils_config_internal.h create mode 100644 src/esp_utils_config_kconfig.h create mode 100644 src/esp_utils_library.h create mode 100644 src/esp_utils_types.h create mode 100644 src/esp_utils_versions.h create mode 100644 src/log/esp_utils_log.c create mode 100644 src/log/esp_utils_log.h create mode 100644 src/memory/esp_utils_memory.c create mode 100644 src/memory/esp_utils_memory.h create mode 100644 test_apps/CMakeLists.txt create mode 100644 test_apps/main/CMakeLists.txt create mode 100644 test_apps/main/idf_component.yml create mode 100644 test_apps/main/test_app_main.cpp create mode 100644 test_apps/main/test_on_c.c create mode 100644 test_apps/main/test_on_cpp.cpp create mode 100644 test_apps/sdkconfig.ci.cxx_exceptions create mode 100644 test_apps/sdkconfig.defaults create mode 100755 tools/check_executables.py create mode 100644 tools/check_file_version.py create mode 100644 tools/executable-list.txt create mode 100644 tools/idf_ci_utils.py diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..79186c6 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,2 @@ +[codespell] +skip = ./src/touch/port/esp_lcd_touch_xpt2046.c diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..648e169 --- /dev/null +++ b/.flake8 @@ -0,0 +1,141 @@ +[flake8] + +select = + # Full lists are given in order to suppress all errors from other plugins + # Full list of pyflakes error codes: + F401, # module imported but unused + F402, # import module from line N shadowed by loop variable + F403, # 'from module import *' used; unable to detect undefined names + F404, # future import(s) name after other statements + F405, # name may be undefined, or defined from star imports: module + F406, # 'from module import *' only allowed at module level + F407, # an undefined __future__ feature name was imported + F601, # dictionary key name repeated with different values + F602, # dictionary key variable name repeated with different values + F621, # too many expressions in an assignment with star-unpacking + F622, # two or more starred expressions in an assignment (a, *b, *c = d) + F631, # assertion test is a tuple, which are always True + F701, # a break statement outside of a while or for loop + F702, # a continue statement outside of a while or for loop + F703, # a continue statement in a finally block in a loop + F704, # a yield or yield from statement outside of a function + F705, # a return statement with arguments inside a generator + F706, # a return statement outside of a function/method + F707, # an except: block as not the last exception handler + F721, F722, # doctest syntax error syntax error in forward type annotation + F811, # redefinition of unused name from line N + F812, # list comprehension redefines name from line N + F821, # undefined name name + F822, # undefined name name in __all__ + F823, # local variable name referenced before assignment + F831, # duplicate argument name in function definition + F841, # local variable name is assigned to but never used + F901, # raise NotImplemented should be raise NotImplementedError + + # Full list of pycodestyle violations: + E101, # indentation contains mixed spaces and tabs + E111, # indentation is not a multiple of four + E112, # expected an indented block + E113, # unexpected indentation + E114, # indentation is not a multiple of four (comment) + E115, # expected an indented block (comment) + E116, # unexpected indentation (comment) + E121, # continuation line under-indented for hanging indent + E122, # continuation line missing indentation or outdented + E123, # closing bracket does not match indentation of opening bracket's line + E124, # closing bracket does not match visual indentation + E125, # continuation line with same indent as next logical line + E126, # continuation line over-indented for hanging indent + E127, # continuation line over-indented for visual indent + E128, # continuation line under-indented for visual indent + E129, # visually indented line with same indent as next logical line + E131, # continuation line unaligned for hanging indent + E133, # closing bracket is missing indentation + E201, # whitespace after '(' + E202, # whitespace before ')' + E203, # whitespace before ':' + E211, # whitespace before '(' + E221, # multiple spaces before operator + E222, # multiple spaces after operator + E223, # tab before operator + E224, # tab after operator + E225, # missing whitespace around operator + E226, # missing whitespace around arithmetic operator + E227, # missing whitespace around bitwise or shift operator + E228, # missing whitespace around modulo operator + E231, # missing whitespace after ',', ';', or ':' + E241, # multiple spaces after ',' + E242, # tab after ',' + E251, # unexpected spaces around keyword / parameter equals + E261, # at least two spaces before inline comment + E262, # inline comment should start with '# ' + E265, # block comment should start with '# ' + E266, # too many leading '#' for block comment + E271, # multiple spaces after keyword + E272, # multiple spaces before keyword + E273, # tab after keyword + E274, # tab before keyword + E275, # missing whitespace after keyword + E301, # expected 1 blank line, found 0 + E302, # expected 2 blank lines, found 0 + E303, # too many blank lines + E304, # blank lines found after function decorator + E305, # expected 2 blank lines after end of function or class + E306, # expected 1 blank line before a nested definition + E401, # multiple imports on one line + E402, # module level import not at top of file + E501, # line too long (82 > 79 characters) + E502, # the backslash is redundant between brackets + E701, # multiple statements on one line (colon) + E702, # multiple statements on one line (semicolon) + E703, # statement ends with a semicolon + E704, # multiple statements on one line (def) + E711, # comparison to None should be 'if cond is None:' + E712, # comparison to True should be 'if cond is True:' or 'if cond:' + E713, # test for membership should be 'not in' + E714, # test for object identity should be 'is not' + E721, # do not compare types, use 'isinstance()' + E722, # do not use bare except, specify exception instead + E731, # do not assign a lambda expression, use a def + E741, # do not use variables named 'l', 'O', or 'I' + E742, # do not define classes named 'l', 'O', or 'I' + E743, # do not define functions named 'l', 'O', or 'I' + E901, # SyntaxError or IndentationError + E902, # IOError + W191, # indentation contains tabs + W291, # trailing whitespace + W292, # no newline at end of file + W293, # blank line contains whitespace + W391, # blank line at end of file + W503, # line break before binary operator + W504, # line break after binary operator + W505, # doc line too long (82 > 79 characters) + W601, # .has_key() is deprecated, use 'in' + W602, # deprecated form of raising exception + W603, # '<>' is deprecated, use '!=' + W604, # backticks are deprecated, use 'repr()' + W605, # invalid escape sequence 'x' + W606, # 'async' and 'await' are reserved keywords starting with Python 3.7 + + # Full list of flake8 violations + E999, # failed to compile a file into an Abstract Syntax Tree for the plugins that require it + + # Full list of mccabe violations + C901 # complexity value provided by the user + +ignore = + E221, # multiple spaces before operator + E231, # missing whitespace after ',', ';', or ':' + E241, # multiple spaces after ',' + W503, # line break before binary operator + W504 # line break after binary operator + +max-line-length = 160 + +show_source = True + +statistics = True + +exclude = + .git, + __pycache__, diff --git a/.github/scripts/check_versions.sh b/.github/scripts/check_lib_versions.sh old mode 100644 new mode 100755 similarity index 89% rename from .github/scripts/check_versions.sh rename to .github/scripts/check_lib_versions.sh index d458178..cdfe6d8 --- a/.github/scripts/check_versions.sh +++ b/.github/scripts/check_lib_versions.sh @@ -13,26 +13,6 @@ check_version_format() { return 0 } -if [ $# -lt 1 ]; then - latest_version="0.0.0" - echo "Don't get the lastest version, use \"0.0.0\" as default" -else - # Get the first input parameter as the version to be compared - latest_version="$1" - # Check the version format - check_version_format "${latest_version}" - result=$? - if [ ${result} -ne 0 ]; then - echo "The latest release version (${latest_version}) format is incorrect." - exit 1 - fi -fi - -# Specify the directory path -target_directory="./" - -echo "Checking directory: ${target_directory}" - # Function: Check if a file exists # Input parameters: $1 The file to check # Return value: 0 if the file exists, 1 if the file does not exist @@ -68,6 +48,32 @@ compare_versions() { return 0 } +# Get the latest release version +latest_version="v0.0.0" +for i in "$@"; do + case $i in + --latest_version=*) + latest_version="${i#*=}" + shift + ;; + *) + ;; + esac +done +# Check the version format +check_version_format "${latest_version}" +result=$? +if [ ${result} -ne 0 ]; then + echo "The latest release version (${latest_version}) format is incorrect." + exit 1 +fi +echo "Get the latest release version: ${latest_version}" + +# Specify the directory path +target_directory="./" + +echo "Checking directory: ${target_directory}" + echo "Checking file: library.properties" # Check if "library.properties" file exists check_file_exists "${target_directory}/library.properties" diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 77ed376..9bc2f2a 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -9,13 +9,13 @@ jobs: build: strategy: matrix: - idf_ver: ["v4.4.5", "v5.0"] - idf_target: ["esp32"] + idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3"] + idf_target: ["esp32", "esp32s3"] runs-on: ubuntu-20.04 container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v3 - - name: Build ESP_Knob Test Application + - name: Build Test Application env: IDF_TARGET: ${{ matrix.idf_target }} working-directory: test_apps @@ -25,4 +25,6 @@ jobs: export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}" - idf.py build \ No newline at end of file + idf.py build + rm -rf sdkconfig build managed_components dependencies.lock + idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.cxx_exceptions" build diff --git a/.github/workflows/check_versions.yml b/.github/workflows/check_lib_versions.yml similarity index 72% rename from .github/workflows/check_versions.yml rename to .github/workflows/check_lib_versions.yml index eb2d116..9203bbb 100644 --- a/.github/workflows/check_versions.yml +++ b/.github/workflows/check_lib_versions.yml @@ -1,11 +1,11 @@ -name: Check Versions +name: Check Library Versions on: pull_request: types: [opened, reopened, synchronize] jobs: - check_versions: + check_lib_versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -26,5 +26,9 @@ jobs: echo "prerelease: ${{ steps.last_release.outputs.prerelease }}" echo "url: ${{ steps.last_release.outputs.url }}" - name: Check & Compare versions - run: bash ./.github/scripts/check_versions.sh ${{ steps.last_release.outputs.tag_name }} - + run: | + if [ ${{ steps.last_release.outputs.tag_name }} == "" ]; then + bash ./.github/scripts/check_lib_versions.sh --latest_version="0.0.0" + else + bash ./.github/scripts/check_lib_versions.sh --latest_version=${{ steps.last_release.outputs.tag_name }} + fi diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 51f77cd..7111ea9 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,4 +11,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 \ No newline at end of file + - uses: pre-commit/action@v2.0.3 diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml new file mode 100644 index 0000000..befc67d --- /dev/null +++ b/.github/workflows/upload_component.yml @@ -0,0 +1,20 @@ +name: Push components to Espressif Component Service + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + with: + submodules: 'recursive' + - name: Upload components to component service + uses: espressif/upload-components-ci-action@v1 + with: + name: "esp-lib-utils" + namespace: "espressif" + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8bb4083..5d79006 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,104 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + repos: -- repo: https://github.com/igrr/astyle_py.git - rev: master + - repo: https://github.com/igrr/astyle_py.git + rev: v1.0.5 hooks: - - id: astyle_py + - id: astyle_py args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper'] -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: trailing-whitespace - types_or: [c, c++] - - id: end-of-file-fixer - types_or: [c, c++] - - id: check-merge-conflict - - id: mixed-line-ending - types_or: [c, c++] - args: ['--fix=lf'] - description: Forces to replace line ending by the UNIX 'lf' character - -- repo: https://github.com/espressif/check-copyright/ - rev: v1.0.3 - hooks: - - id: check-copyright - args: ['--config', 'check_copyright_config.yaml'] \ No newline at end of file + - repo: https://github.com/espressif/check-copyright/ + rev: v1.0.3 + hooks: + - id: check-copyright + args: ['--config', 'check_copyright_config.yaml'] + + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 + hooks: + - id: flake8 + types: [python] + args: ['--config=.flake8', '--tee', '--benchmark'] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + # note: whitespace exclusions use multiline regex, see https://pre-commit.com/#regular-expressions + # items are: + # 1 - some file extensions + # 2 - any file matching *test*/*expected* (for host tests, if possible use this naming pattern always) + # 3 - any file with known-warnings in the name + # 4 - any directory named 'testdata' + # 5 - protobuf auto-generated files + exclude: &whitespace_excludes | + (?x)^( + .+\.(md|rst|map|bin)| + .+test.*\/.*expected.*| + .+known-warnings.*| + .+\/testdata\/.+| + .*_pb2.py| + .*.pb-c.h| + .*.pb-c.c| + .*.yuv + )$ + - id: end-of-file-fixer + exclude: *whitespace_excludes + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: mixed-line-ending + args: ['-f=lf'] + - id: double-quote-string-fixer + - id: no-commit-to-branch + name: Do not use more than one slash in the branch name + args: ['--pattern', '^[^/]*/[^/]*/'] + - id: no-commit-to-branch + name: Do not use uppercase letters in the branch name + args: ['--pattern', '^[^A-Z]*[A-Z]'] + + - repo: https://github.com/espressif/conventional-precommit-linter + rev: v1.8.0 + hooks: + - id: conventional-precommit-linter + stages: [commit-msg] + args: + - --subject-min-length=15 + - --body-max-line-length=200 + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ['-w' , '--config', '.codespellrc'] + + - repo: local + hooks: + - id: check-executables + name: Check File Permissions + entry: tools/check_executables.py --action executables + language: python + types: [executable] + exclude: '\.pre-commit/.+' + - id: check-executable-list + name: Validate executable-list.txt + entry: tools/check_executables.py --action list + language: python + pass_filenames: false + always_run: true + + - repo: local + hooks: + - id: check-file-versions + name: Update when versions change + entry: python3 tools/check_file_version.py ./ + language: system + files: '(library.properties|.*esp_utils_versions.h)' + + - repo: local + hooks: + - id: check-library-versions + name: Check library versions + entry: ./.github/scripts/check_lib_versions.sh + language: system + files: '(idf_component.yml|library.properties)' diff --git a/CHANGELOG.md b/CHANGELOG.md index 8afa927..328aa2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # ChangeLog -## v0.0.1 - [xxx] +## v0.1.0 ### Enhancements: -* [xxx] +* feat(repo): initialize with modules 'check', 'log' and 'utils' diff --git a/CMakeLists.txt b/CMakeLists.txt index e2a37db..f215245 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ -idf_component_register(SRCS [xxx] - INCLUDE_DIRS [xxx] - REQUIRES [xxx] - PRIV_REQUIRES [xxx]) +set(SRC_DIR ./src) +file(GLOB_RECURSE SRCS ${SRC_DIR}/*.c) + +idf_component_register( + SRCS ${SRCS} + INCLUDE_DIRS ${SRC_DIR} +) diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..4ebe272 --- /dev/null +++ b/Kconfig @@ -0,0 +1,111 @@ +menu "ESP Library Utils" + config ESP_UTILS_CONFIG_FILE_SKIP + bool "Unckeck this to ignore `esp_utils_config.h`" + default y + + menu "Check functions" + config ESP_UTILS_ENABLE_CHECK + bool "Enable error check" + default y + help + If enabled, the driver will check the function parameters, return value, etc. Disable them will reduce the code size. + + config ESP_UTILS_CHECK_WITH_ERROR_LOG + bool "Print error message on error" + default y + depends on ESP_UTILS_ENABLE_CHECK + help + If enabled, the driver will print error message when check failed. + + config ESP_UTILS_CHECK_WITH_ASSERT + bool "Assert on error" + default n + depends on ESP_UTILS_ENABLE_CHECK + help + If enabled, the driver will assert when check failed. + endmenu + + menu "Log functions" + config ESP_UTILS_ENABLE_LOG + bool "Enable output debug log" + default y + help + If enabled, the driver will output log for debugging. + + config ESP_UTILS_LOG_BUFFER_SIZE + int "Buffer size for log messages" + depends on ESP_UTILS_ENABLE_LOG + default 256 + + choice ESP_UTILS_LOG_LEVEL + prompt "Log level for all messages" + depends on ESP_UTILS_ENABLE_LOG + default ESP_UTILS_LOG_LEVEL_INFO + + config ESP_UTILS_LOG_LEVEL_DEBUG + bool "Debug" + help + Extra information which is not necessary for normal use (values, pointers, sizes, etc) + + config ESP_UTILS_LOG_LEVEL_INFO + bool "Info" + help + Information messages which describe the normal flow of events + + config ESP_UTILS_LOG_LEVEL_WARNING + bool "Warning" + help + Error conditions from which recovery measures have been taken + + config ESP_UTILS_LOG_LEVEL_ERROR + bool "Error" + help + Critical errors, software module cannot recover on its own + endchoice + + config ESP_UTILS_LOG_GLOBAL_LEVEL + int + default 0 if ESP_UTILS_LOG_LEVEL_DEBUG + default 1 if ESP_UTILS_LOG_LEVEL_INFO + default 2 if ESP_UTILS_LOG_LEVEL_WARNING + default 3 if ESP_UTILS_LOG_LEVEL_ERROR + + config ESP_UTILS_ENABLE_LOG_TRACE + bool "Enable trace function" + depends on ESP_UTILS_LOG_LEVEL_DEBUG + default n + help + If enabled, the driver will print trace log messages when enter/exit functions, useful for debugging + endmenu + + menu "Memory functions" + choice ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CHOICE + prompt "Select general allocation type" + default ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_STDLIB + + config ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_STDLIB + bool "Standard library (malloc, free)" + + config ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_ESP + bool "ESP (heap_caps_malloc, free)" + + config ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_MICROPYTHON + bool "MicroPython (mp_malloc, mp_free)" + + config ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM + bool "Custom (`ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_MALLOC` and `ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_FREE`)" + endchoice + + config ESP_UTILS_MEM_GENERAL_ALLOC_TYPE + int + default 0 if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_STDLIB + default 1 if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_ESP + default 2 if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_MICROPYTHON + default 3 if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM + + config ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE + string "General custom memory header file" + depends on ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM + default "stdlib.h" + endmenu +endmenu diff --git a/README.md b/README.md index 1f4bb80..ac3b0ff 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,40 @@ [![Arduino Lint](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/arduino_lint.yml/badge.svg)](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/arduino_lint.yml) [![pre-commit](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/pre-commit.yml) [![Build Test Apps](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/build_test.yml/badge.svg)](https://github.com/esp-arduino-libs/[xxx]/actions/workflows/build_test.yml) -# [xxx] +# ESP Library Utils -[xxx] is an Arduino library designed for driving [xxx] using ESP SoCs. - -[xxx] encapsulates the component from the [Espressif Components Registry](https://components.espressif.com/). It is developed based on [arduino-esp32](https://github.com/espressif/arduino-esp32) and can be easily downloaded and integrated into the Arduino IDE. - -## Features - -* [xxx] - -## Supported Drivers - -| **Driver** | **Version** | -| ------------------------------------------------------------------ | ----------- | -| [xxx](https://components.espressif.com/components/espressif/xxx) | | - -## Dependencies Version - -| **Name** | **Version** | -| ----------------------------------------------------------- | ----------- | -| [xxx] | v0.x.x | -| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= [xxx] | +esp-lib-utils is a library designed for ESP SoCs to provide utility functions, including `logging`, `memory management`, and `checking`. ## How to Use -For information on how to use the library in the Arduino IDE, please refer to the documentation for [Arduino IDE v1.x.x](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries) or [Arduino IDE v2.x.x](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library). - -### Examples - -* [xxx](examples/xxx): Demonstrates how to use [xxx] and test all functions. - -### Detailed Usage - ```cpp - [xxx] +// Define the log tag for the current library, should be declared before `esp_utils_library.h` +#define ESP_UTILS_LOG_TAG "MyLibrary" +#include "esp_utils_library.h" + +void test_log(void) +{ + ESP_UTILS_LOG_TRACE_ENTER(); + + ESP_UTILS_LOGD("This is a debug message"); + ESP_UTILS_LOGI("This is an info message"); + ESP_UTILS_LOGW("This is a warning message"); + ESP_UTILS_LOGE("This is an error message"); + + ESP_UTILS_LOG_TRACE_EXIT(); +} + +void test_memory(void) +{ + // Allocate memory in C style (`malloc/calloc` and `free` are re-defined by the library) + int *c_ptr = (int *)malloc(sizeof(int)); + ESP_UTILS_CHECK_NULL_EXIT(c_ptr, "Failed to allocate memory"); + free(c_ptr); + + // Allocate memory in C++ style + std::shared_ptr cxx_ptr = nullptr; + ESP_UTILS_CHECK_EXCEPTION_EXIT( + (cxx_ptr = esp_utils::make_shared()), "Failed to allocate memory" + ); + cxx_ptr = nullptr; +} ``` diff --git a/check_copyright_config.yaml b/check_copyright_config.yaml index d0c1e09..fc60792 100644 --- a/check_copyright_config.yaml +++ b/check_copyright_config.yaml @@ -28,14 +28,13 @@ DEFAULT: # You can create your own rules for files or group of files examples_and_unit_tests: include: - - 'test_apps/' + - 'test_apps/' allowed_licenses: - Apache-2.0 - Unlicense - CC0-1.0 license_for_new_files: CC0-1.0 -ignore: # You can also select ignoring files here - perform_check: no # Don't check files from that block - include: - - 'examples/' \ No newline at end of file +# ignore: # You can also select ignoring files here +# perform_check: no # Don't check files from that block +# include: diff --git a/esp_utils_config.h b/esp_utils_config.h new file mode 100644 index 0000000..283b478 --- /dev/null +++ b/esp_utils_config.h @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// *INDENT-OFF* + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////// Utils Configurations ///////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Check functions related. + * + * These functions are used to check the function parameters, return value, etc. Disable them will reduce the code size. + * + */ +/* Set to 1 if use `ESP_UTILS_CHECK_*()` macros */ +#define ESP_UTILS_ENABLE_CHECK (1) // 0/1 +#if ESP_UTILS_ENABLE_CHECK + /* Set to 1 if print message when check failed */ + #define ESP_UTILS_CHECK_WITH_ERROR_LOG (1) // 0/1 + + /* Set to 1 if assert when check failed */ + #define ESP_UTILS_CHECK_WITH_ASSERT (0) // 0/1 +#endif // ESP_UTILS_ENABLE_CHECK + +/** + * Log functions related. + * + * These functions are used to print log messages. Disable them will reduce the code size. + * + */ +/* Set to 1 if use `ESP_UTILS_LOG*()` macros */ +#define ESP_UTILS_ENABLE_LOG (1) // 0/1 +#if ESP_UTILS_ENABLE_LOG + /** + * Global log level, logs with a level lower than this will not be compiled. Choose one of the following: + * - ESP_UTILS_LOG_LEVEL_DEBUG: Extra information which is not necessary for normal use (values, pointers, sizes, + * etc) (lowest level) + * - ESP_UTILS_LOG_LEVEL_INFO: Information messages which describe the normal flow of events + * - ESP_UTILS_LOG_LEVEL_WARNING: Error conditions from which recovery measures have been taken + * - ESP_UTILS_LOG_LEVEL_ERROR: Critical errors, software module cannot recover on its own (highest level) + * + */ + #define ESP_UTILS_LOG_GLOBAL_LEVEL ESP_UTILS_LOG_LEVEL_INFO + #if ESP_UTILS_LOG_GLOBAL_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG + /* Set to 1 if print trace log messages when enter/exit functions, useful for debugging */ + #define ESP_UTILS_ENABLE_LOG_TRACE (0) + #endif // ESP_UTILS_LOG_GLOBAL_LEVEL + + /* Log format buffer size */ + #define ESP_UTILS_LOG_BUFFER_SIZE (256) +#endif // ESP_UTILS_ENABLE_LOG + +/** + * Memory related + * + */ +/** + * Memory allocation type, choose one of the following: + * - ESP_UTILS_MEM_ALLOC_TYPE_STDLIB: Use the standard library memory allocation functions (malloc, free) + * - ESP_UTILS_MEM_ALLOC_TYPE_ESP: Use the ESP-IDF memory allocation functions (heap_caps_malloc, heap_caps_free) + * - ESP_UTILS_MEM_ALLOC_TYPE_MICROPYTHON: Use the MicroPython memory allocation functions (m_malloc, m_free) + * - ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM: Use custom memory allocation functions (ESP_UTILS_MEM_ALLOC_CUSTOM_MALLOC, + * ESP_UTILS_MEM_ALLOC_CUSTOM_FREE) + * + */ +#define ESP_UTILS_MEM_GENERAL_ALLOC_TYPE ESP_UTILS_MEM_ALLOC_TYPE_STDLIB +#if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_ESP + + #define ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS (MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT) + +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM + + #define ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE stdlib.h + #define ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_MALLOC malloc + #define ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_FREE free + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////// File Version /////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Do not change the following versions, they are used to check if the configurations in this file are compatible with + * the current version of `esp_utils_config.h` in the library. The detailed rules are as follows: + * + * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library + * and must be replaced with the file from the library. + * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to + * default values. It is recommended to replace it with the file from the library. + * 3. Even if the patch version is not consistent, it will not affect normal functionality. + * + */ +#define ESP_UTILS_CONFIG_FILE_VERSION_MAJOR 0 +#define ESP_UTILS_CONFIG_FILE_VERSION_MINOR 1 +#define ESP_UTILS_CONFIG_FILE_VERSION_PATCH 0 + +// *INDENT-OFF* diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..4ff3178 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,7 @@ +version: "0.1.0" +description: esp-lib-utils is a library designed for ESP SoCs to provide utility functions, including logging, checking, and memory management. +url: https://github.com/esp-arduino-libs/esp-lib-utils +repository: https://github.com/esp-arduino-libs/esp-lib-utils.git +issues: https://github.com/esp-arduino-libs/esp-lib-utils/issues +dependencies: + idf: ">=5.1" diff --git a/library.properties b/library.properties index 98e8a6f..854123f 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ -name=[xxx] -version=0.0.1 +name=esp-lib-utils +version=0.1.0 author=espressif -maintainer=[xxx] -sentence=[xxx] is a library designed for [xxx] using ESP SoCs -paragraph=Currently support [xxx] +maintainer=espressif +sentence=esp-lib-utils is a library designed for ESP SoCs to provide utility functions, including logging, checking, and memory management. +paragraph= category=Other architectures=esp32 -url=[xxx] -includes=[xxx] +url=https://github.com/esp-arduino-libs/esp-lib-utils +includes=esp_utils_library.h diff --git a/src/check/esp_utils_check.h b/src/check/esp_utils_check.h new file mode 100644 index 0000000..c3aebed --- /dev/null +++ b/src/check/esp_utils_check.h @@ -0,0 +1,425 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_err.h" +#include "esp_utils_config_internal.h" +#include "log/esp_utils_log.h" + +#if !ESP_UTILS_ENABLE_CHECK + +#ifndef ESP_UTILS_CHECK_TAG +#define ESP_UTILS_CHECK_TAG(goto_tag) +#endif + +#define ESP_UTILS_CHECK_NULL_RETURN(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_NULL_GOTO(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_NULL_EXIT(x, ...) ((void)(x)) + +#define ESP_UTILS_CHECK_FALSE_RETURN(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_FALSE_GOTO(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_FALSE_EXIT(x, ...) ((void)(x)) + +#define ESP_UTILS_CHECK_ERROR_RETURN(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_ERROR_GOTO(x, ...) ((void)(x)) +#define ESP_UTILS_CHECK_ERROR_EXIT(x, ...) ((void)(x)) + +#ifdef __cplusplus +#define ESP_UTILS_CHECK_EXCEPTION_RETURN(x, ret, fmt, ...) ((void)(x)) +#define ESP_UTILS_CHECK_EXCEPTION_GOTO(x, goto_tag, fmt, ...) ((void)(x)) +#define ESP_UTILS_CHECK_EXCEPTION_EXIT(x, fmt, ...) ((void)(x)) +#endif // __cplusplus + +#else + +#if ESP_UTILS_CHECK_WITH_ASSERT + +#ifndef ESP_UTILS_CHECK_TAG +#define ESP_UTILS_CHECK_TAG(goto_tag) +#endif + +#define ESP_UTILS_CHECK_NULL_RETURN(x, ...) assert((x) != NULL) +#define ESP_UTILS_CHECK_NULL_GOTO(x, ...) assert((x) != NULL) +#define ESP_UTILS_CHECK_NULL_EXIT(x, ...) assert((x) != NULL) + +#define ESP_UTILS_CHECK_FALSE_RETURN(x, ...) assert(x) +#define ESP_UTILS_CHECK_FALSE_GOTO(x, ...) assert(x) +#define ESP_UTILS_CHECK_FALSE_EXIT(x, ...) assert(x) + +#define ESP_UTILS_CHECK_ERROR_RETURN(x, ...) assert((x) != ESP_OK) +#define ESP_UTILS_CHECK_ERROR_GOTO(x, ...) assert((x) != ESP_OK) +#define ESP_UTILS_CHECK_ERROR_EXIT(x, ...) assert((x) != ESP_OK) + +#ifdef __cplusplus +#define ESP_UTILS_CHECK_EXCEPTION_RETURN(x, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + assert(false); \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_GOTO(x, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + assert(false); \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_EXIT(x, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + assert(false); \ + } \ + } while (0) +#endif // __cplusplus + +#else + +#ifndef unlikely +#define unlikely(x) (x) +#endif + +#ifndef ESP_UTILS_CHECK_TAG +#define ESP_UTILS_CHECK_TAG(goto_tag) goto_tag: +#endif + +#if ESP_UTILS_CHECK_WITH_ERROR_LOG + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and return the specified value. + * + * @param x Pointer to check + * @param ret Value to return if the pointer is NULL + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_RETURN(x, ret, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and goto the specified label. + * + * @param x Pointer to check + * @param goto_tag Label to jump to if the pointer is NULL + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and return without a value. + * + * @param x Pointer to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_EXIT(x, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and return the specified value. + * + * @param x Value to check + * @param ret Value to return if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_RETURN(x, ret, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and goto the specified label. + * + * @param x Value to check + * @param goto_tag Label to jump to if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and return without a value. + * + * @param x Value to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_EXIT(x, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and return the specified value. + * + * @param x Value to check + * @param ret Value to return if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_RETURN(x, ret, fmt, ...) do { \ + esp_err_t err = (x); \ + if (unlikely(err != ESP_OK)) { \ + ESP_UTILS_LOGE(fmt " [%s]", ##__VA_ARGS__, esp_err_to_name(err)); \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and goto the specified label. + * + * @param x Value to check + * @param goto_tag Label to jump to if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) != ESP_OK)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and return without a value. + * + * @param x Value to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_EXIT(x, fmt, ...) do { \ + if (unlikely((x) != ESP_OK)) { \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return; \ + } \ + } while(0) + +#ifdef __cplusplus +/** + * The `try {} catch {}` block is only available in C++ and `CONFIG_COMPILER_CXX_EXCEPTIONS = 1` + * + */ +#if CONFIG_COMPILER_CXX_EXCEPTIONS + +#define ESP_UTILS_CHECK_EXCEPTION_RETURN(x, ret, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + ESP_UTILS_LOGE("Exception caught: %s", e.what()); \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return ret; \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_GOTO(x, goto_tag, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + ESP_UTILS_LOGE("Exception caught: %s", e.what()); \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_EXIT(x, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + ESP_UTILS_LOGE("Exception caught: %s", e.what()); \ + ESP_UTILS_LOGE(fmt, ##__VA_ARGS__); \ + return; \ + } \ + } while (0) + +#else + +#define ESP_UTILS_CHECK_EXCEPTION_RETURN(x, ret, fmt, ...) (x) +#define ESP_UTILS_CHECK_EXCEPTION_GOTO(x, goto_tag, fmt, ...) (x) +#define ESP_UTILS_CHECK_EXCEPTION_EXIT(x, fmt, ...) (x) + +#endif // CONFIG_COMPILER_CXX_EXCEPTIONS +#endif // __cplusplus + +#else + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and return the specified value. + * + * @param x Pointer to check + * @param ret Value to return if the pointer is NULL + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_RETURN(x, ret, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and goto the specified label. + * + * @param x Pointer to check + * @param goto_tag Label to jump to if the pointer is NULL + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the pointer is NULL; if NULL, log an error and return without a value. + * + * @param x Pointer to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_NULL_EXIT(x, fmt, ...) do { \ + if (unlikely((x) == NULL)) { \ + return; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and return the specified value. + * + * @param x Value to check + * @param ret Value to return if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_RETURN(x, ret, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and goto the specified label. + * + * @param x Value to check + * @param goto_tag Label to jump to if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the value is false; if false, log an error and return without a value. + * + * @param x Value to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_FALSE_EXIT(x, fmt, ...) do { \ + if (unlikely((x) == false)) { \ + return; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and return the specified value. + * + * @param x Value to check + * @param ret Value to return if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_RETURN(x, ret, fmt, ...) do { \ + esp_err_t err = (x); \ + if (unlikely(err != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and goto the specified label. + * + * @param x Value to check + * @param goto_tag Label to jump to if the value is false + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_GOTO(x, goto_tag, fmt, ...) do { \ + if (unlikely((x) != ESP_OK)) { \ + goto goto_tag; \ + } \ + } while(0) + +/** + * @brief Check if the value is not `ESP_OK`; if not, log an error and return without a value. + * + * @param x Value to check + * @param fmt Format string for the error message + * @param ... Additional arguments for the format string + */ +#define ESP_UTILS_CHECK_ERROR_EXIT(x, fmt, ...) do { \ + if (unlikely((x) != ESP_OK)) { \ + return; \ + } \ + } while(0) + +#ifdef __cplusplus +#define ESP_UTILS_CHECK_EXCEPTION_RETURN(x, ret, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + return ret; \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_GOTO(x, goto_tag, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + goto goto_tag; \ + } \ + } while (0) + +#define ESP_UTILS_CHECK_EXCEPTION_EXIT(x, fmt, ...) do {\ + try { \ + x; \ + } catch (const std::exception &e) { \ + return; \ + } \ + } while (0) +#endif // __cplusplus + +#endif // ESP_UTILS_CHECK_WITH_ERROR_LOG +#endif // ESP_UTILS_CHECK_WITH_ASSERT +#endif // ESP_UTILS_ENABLE_CHECK diff --git a/src/esp_utils_config_internal.h b/src/esp_utils_config_internal.h new file mode 100644 index 0000000..a39cf69 --- /dev/null +++ b/src/esp_utils_config_internal.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// *INDENT-OFF* + +/* Handle special Kconfig options */ +#ifndef ESP_UTILS_KCONFIG_IGNORE + #include "sdkconfig.h" + #ifdef CONFIG_ESP_UTILS_CONFIG_FILE_SKIP + #define ESP_UTILS_CONFIG_FILE_SKIP + #endif +#endif + +#ifndef ESP_UTILS_CONFIG_FILE_SKIP +/* If "esp_utils_config.h" is available from here, try to use it later */ +#ifdef __has_include + #if __has_include("esp_utils_config.h") + #ifndef ESP_UTILS_CONFIG_INCLUDE_SIMPLE + #define ESP_UTILS_CONFIG_INCLUDE_SIMPLE + #endif + #elif __has_include("../../esp_utils_config.h") + #ifndef ESP_UTILS_CONFIG_INCLUDE_OUTSIDE + #define ESP_UTILS_CONFIG_INCLUDE_OUTSIDE + #endif + #else + #define ESP_UTILS_CONFIG_INCLUDE_INSIDE + #endif +#endif +#else +#endif + +/* If "esp_utils_config.h" is not skipped, include it */ +#ifndef ESP_UTILS_CONFIG_FILE_SKIP + #ifdef ESP_UTILS_CONFIG_PATH /* If there is a path defined for "esp_utils_config.h" use it */ + #define __TO_STR_AUX(x) #x + #define __TO_STR(x) __TO_STR_AUX(x) + #include __TO_STR(ESP_UTILS_CONFIG_PATH) + #undef __TO_STR_AUX + #undef __TO_STR + #elif defined(ESP_UTILS_CONFIG_INCLUDE_SIMPLE) /* Or simply include if "esp_utils_config.h" is available */ + #include "esp_utils_config.h" + #elif defined(ESP_UTILS_CONFIG_INCLUDE_OUTSIDE) /* Or include if "../../esp_utils_config.h" is available */ + #include "../../esp_utils_config.h" + #elif defined(ESP_UTILS_CONFIG_INCLUDE_INSIDE) /* Or include the default configuration */ + #include "../esp_utils_config.h" + #endif +#endif + +#include "esp_utils_types.h" + +#ifndef ESP_UTILS_CONFIG_INCLUDE_INSIDE + /** + * There are two purposes to include the this file: + * 1. Convert configuration items starting with `CONFIG_` to the required configuration items. + * 2. Define default values for configuration items that are not defined to keep compatibility. + * + */ + #include "esp_utils_config_kconfig.h" +#endif + +// *INDENT-OFF* diff --git a/src/esp_utils_config_kconfig.h b/src/esp_utils_config_kconfig.h new file mode 100644 index 0000000..f0e8238 --- /dev/null +++ b/src/esp_utils_config_kconfig.h @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// *INDENT-OFF* + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////// Check Configurations ///////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Check functions related. + * + */ +#ifndef ESP_UTILS_ENABLE_CHECK + #ifdef CONFIG_ESP_UTILS_ENABLE_CHECK + #define ESP_UTILS_ENABLE_CHECK CONFIG_ESP_UTILS_ENABLE_CHECK + #else + #define ESP_UTILS_ENABLE_CHECK (0) + #endif +#endif +#ifndef ESP_UTILS_CHECK_WITH_ERROR_LOG + #ifdef CONFIG_ESP_UTILS_CHECK_WITH_ERROR_LOG + #define ESP_UTILS_CHECK_WITH_ERROR_LOG CONFIG_ESP_UTILS_CHECK_WITH_ERROR_LOG + #else + #define ESP_UTILS_CHECK_WITH_ERROR_LOG (0) + #endif +#endif +#ifndef ESP_UTILS_CHECK_WITH_ASSERT + #ifdef CONFIG_ESP_UTILS_CHECK_WITH_ASSERT + #define ESP_UTILS_CHECK_WITH_ASSERT CONFIG_ESP_UTILS_CHECK_WITH_ASSERT + #else + #define ESP_UTILS_CHECK_WITH_ASSERT (0) + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////// LOG Configurations ////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef ESP_UTILS_ENABLE_LOG + #ifdef CONFIG_ESP_UTILS_ENABLE_LOG + #define ESP_UTILS_ENABLE_LOG CONFIG_ESP_UTILS_ENABLE_LOG + #else + #define ESP_UTILS_ENABLE_LOG (0) + #endif +#endif + +#ifndef ESP_UTILS_LOG_BUFFER_SIZE + #ifdef CONFIG_ESP_UTILS_LOG_BUFFER_SIZE + #define ESP_UTILS_LOG_BUFFER_SIZE CONFIG_ESP_UTILS_LOG_BUFFER_SIZE + #else + #define ESP_UTILS_LOG_BUFFER_SIZE (256) + #endif +#endif + +#ifndef ESP_UTILS_LOG_GLOBAL_LEVEL + #ifdef CONFIG_ESP_UTILS_LOG_GLOBAL_LEVEL + #define ESP_UTILS_LOG_GLOBAL_LEVEL CONFIG_ESP_UTILS_LOG_GLOBAL_LEVEL + #else + #define ESP_UTILS_LOG_GLOBAL_LEVEL ESP_UTILS_LOG_LEVEL_INFO + #endif +#endif + +#ifndef ESP_UTILS_ENABLE_LOG_TRACE + #ifdef CONFIG_ESP_UTILS_ENABLE_LOG_TRACE + #define ESP_UTILS_ENABLE_LOG_TRACE CONFIG_ESP_UTILS_ENABLE_LOG_TRACE + #else + #define ESP_UTILS_ENABLE_LOG_TRACE 0 + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////// Memory Configurations ///////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef ESP_UTILS_MEM_GENERAL_ALLOC_TYPE + #ifdef CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_TYPE + #define ESP_UTILS_MEM_GENERAL_ALLOC_TYPE CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_TYPE + #else + #define ESP_UTILS_MEM_GENERAL_ALLOC_TYPE (ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_STDLIB) + #endif +#endif + + +#if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_ESP + #ifndef ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS + #ifdef CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS + #define ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS + #else + #error "`ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS` must be defined when `ESP_UTILS_MEM_GENERAL_ALLOC_TYPE` is set to `ESP_UTILS_MEM_ALLOC_TYPE_ESP`" + #endif + #endif +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM + #ifndef ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE + #ifdef CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE + #define ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE CONFIG_ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE + #else + #define ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE "stdlib.h" + #endif + #endif + + #ifndef ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_MALLOC + #error "`ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_MALLOC` must be defined when `ESP_UTILS_MEM_GENERAL_ALLOC_TYPE` is set to `ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM`" + #endif + + #ifndef ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_FREE + #error "`ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_FREE` must be defined when `ESP_UTILS_MEM_GENERAL_ALLOC_TYPE` is set to `ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM`" + #endif +#endif diff --git a/src/esp_utils_library.h b/src/esp_utils_library.h new file mode 100644 index 0000000..690f523 --- /dev/null +++ b/src/esp_utils_library.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_utils_types.h" +#include "esp_utils_versions.h" +#include "check/esp_utils_check.h" +#include "log/esp_utils_log.h" +#include "memory/esp_utils_memory.h" diff --git a/src/esp_utils_types.h b/src/esp_utils_types.h new file mode 100644 index 0000000..3cbb70c --- /dev/null +++ b/src/esp_utils_types.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" + +/** + * @brief Macros for log level + * + */ +#define ESP_UTILS_LOG_LEVEL_DEBUG (0) /*!< Extra information which is not necessary for normal use (values, + * pointers, sizes, etc). */ +#define ESP_UTILS_LOG_LEVEL_INFO (1) /*!< Information messages which describe the normal flow of events */ +#define ESP_UTILS_LOG_LEVEL_WARNING (2) /*!< Error conditions from which recovery measures have been taken */ +#define ESP_UTILS_LOG_LEVEL_ERROR (3) /*!< Critical errors, software module cannot recover on its own */ + +/** + * @brief Macros for memory type + * + */ +#define ESP_UTILS_MEM_ALLOC_TYPE_STDLIB (0) +#define ESP_UTILS_MEM_ALLOC_TYPE_ESP (1) +#define ESP_UTILS_MEM_ALLOC_TYPE_MICROPYTHON (2) +#define ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM (3) diff --git a/src/esp_utils_versions.h b/src/esp_utils_versions.h new file mode 100644 index 0000000..bad97ab --- /dev/null +++ b/src/esp_utils_versions.h @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_utils_config_internal.h" + +/* Library Version */ +#define ESP_UTILS_VERSION_MAJOR 0 +#define ESP_UTILS_VERSION_MINOR 1 +#define ESP_UTILS_VERSION_PATCH 0 + +/* File `esp_utils_config.h` */ +#define ESP_UTILS_CONFIG_VERSION_MAJOR 0 +#define ESP_UTILS_CONFIG_VERSION_MINOR 1 +#define ESP_UTILS_CONFIG_VERSION_PATCH 0 + +// *INDENT-OFF* + +/** + * Check if the current configuration file version is compatible with the library version + * + */ +/* File `esp_utils_config.h` */ +#ifndef ESP_UTILS_CONFIG_FILE_SKIP + // Check if the current configuration file version is compatible with the library version + #if ESP_UTILS_CONFIG_FILE_VERSION_MAJOR != ESP_UTILS_CONFIG_VERSION_MAJOR + #error "The file `esp_utils_config.h` version is not compatible. Please update it with the file from the library" + #elif ESP_UTILS_CONFIG_FILE_VERSION_MINOR < ESP_UTILS_CONFIG_VERSION_MINOR + #warning "The file `esp_utils_config.h` version is outdated. Some new configurations are missing" + #elif ESP_UTILS_CONFIG_FILE_VERSION_PATCH > ESP_UTILS_VERSION_PATCH + #warning "The file `esp_utils_config.h` version is newer than the library. Some new configurations are not supported" + #endif /* ESP_UTILS_CONFIG_INCLUDE_INSIDE */ +#endif /* ESP_UTILS_CONFIG_FILE_SKIP */ + +// *INDENT-OFF* diff --git a/src/log/esp_utils_log.c b/src/log/esp_utils_log.c new file mode 100644 index 0000000..7e03502 --- /dev/null +++ b/src/log/esp_utils_log.c @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "esp_utils_config_internal.h" + +#if ESP_UTILS_ENABLE_LOG +/** + * @brief Extract filename from file path + * + * @param file_path File path + * @return File name + */ +const char *esp_utils_log_extract_file_name(const char *file_path) +{ + const char *filename = strrchr(file_path, '/'); + if (!filename) { + filename = strrchr(file_path, '\\'); // Windows path compatibility + } + + return filename ? filename + 1 : file_path; +} +#endif // ESP_UTILS_ENABLE_LOG diff --git a/src/log/esp_utils_log.h b/src/log/esp_utils_log.h new file mode 100644 index 0000000..2e3308f --- /dev/null +++ b/src/log/esp_utils_log.h @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_utils_config_internal.h" + +#ifndef ESP_UTILS_LOG_TAG +#define ESP_UTILS_LOG_TAG "Utils" +#endif + +#ifdef __cplusplus + +#include +#include +#include +#include +#include + +extern "C" const char *esp_utils_log_extract_file_name(const char *file_path); + +namespace esp_utils { + +/** + * Class to handle logging + * + */ +class Log { +public: + // Singleton pattern: Get the unique instance of the class + static Log &getInstance() + { + static Log instance; + return instance; + } + +#if ESP_UTILS_ENABLE_LOG + // Templates and conditional compilation: Filter logs by different levels + template + void print(const char *file, int line, const char *func, const char *format, ...) + { + // Logs below the global level will not be compiled + if constexpr (level >= ESP_UTILS_LOG_GLOBAL_LEVEL) { + // Mutex to avoid interleaved log messages + _mutex.lock(); + // Use variadic arguments for formatted output + va_list args; + va_start(args, format); + vsnprintf(_buffer, sizeof(_buffer), format, args); + va_end(args); + // Output log message + printf( + "[%s][%s][%s:%04d](%s): %s\n", ESP_UTILS_LOG_TAG, logLevelToString(level), + esp_utils_log_extract_file_name(file), line, func, _buffer + ); + _mutex.unlock(); + } + } +#else + // When logging is disabled, the `print` function has an empty implementation + template + void print(const char *, int, const char *, const char *, ...) const {} +#endif /* ESP_UTILS_ENABLE_LOG */ + +private: + Log() = default; + +#if ESP_UTILS_ENABLE_LOG + // Convert log level to string + static const char *logLevelToString(int level) + { + switch (level) { + case ESP_UTILS_LOG_LEVEL_DEBUG: return "DEBUG"; + case ESP_UTILS_LOG_LEVEL_INFO: return " INFO"; + case ESP_UTILS_LOG_LEVEL_WARNING: return " WARN"; + case ESP_UTILS_LOG_LEVEL_ERROR: return "ERROR"; + default: break; + } + return "UNKNOWN"; + } + + char _buffer[ESP_UTILS_LOG_BUFFER_SIZE]; + std::mutex _mutex; +#endif +}; + +} // namespace esp_utils + +/** + * Macros to simplify logging calls + * + */ +#define ESP_UTILS_LOGD(format, ...) \ + esp_utils::Log::getInstance().print(__FILE__, __LINE__, __func__, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGI(format, ...) \ + esp_utils::Log::getInstance().print(__FILE__, __LINE__, __func__, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGW(format, ...) \ + esp_utils::Log::getInstance().print(__FILE__, __LINE__, __func__, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGE(format, ...) \ + esp_utils::Log::getInstance().print(__FILE__, __LINE__, __func__, format, ##__VA_ARGS__) + +/** + * Micros to log trace of function calls + * + */ +#if ESP_UTILS_ENABLE_LOG_TRACE +#define ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS() ESP_UTILS_LOGD("(@%p) Enter", this) +#define ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS() ESP_UTILS_LOGD("(@%p) Exit", this) +#else +#define ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS() +#define ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS() +#endif + +#else + +#include +#include + +const char *esp_utils_log_extract_file_name(const char *file_path); + +#define ESP_UTILS_IMPL_LOGD(format, ...) printf("[" ESP_UTILS_LOG_TAG "][DEBUG][%s:%04d](%s): " format "\n", \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_IMPL_LOGI(format, ...) printf("[" ESP_UTILS_LOG_TAG "][ INFO][%s:%04d](%s): " format "\n", \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_IMPL_LOGW(format, ...) printf("[" ESP_UTILS_LOG_TAG "][ WARN][%s:%04d](%s): " format "\n", \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_IMPL_LOGE(format, ...) printf("[" ESP_UTILS_LOG_TAG "][ERROR][%s:%04d](%s): " format "\n", \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) + +#define ESP_UTILS_LOG_LEVEL(level, format, ...) do { \ + if (level == ESP_UTILS_LOG_LEVEL_DEBUG) { ESP_UTILS_IMPL_LOGD(format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_INFO) { ESP_UTILS_IMPL_LOGI(format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_WARNING) { ESP_UTILS_IMPL_LOGW(format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_ERROR) { ESP_UTILS_IMPL_LOGE(format, ##__VA_ARGS__); } \ + else { } \ + } while(0) + +#define ESP_UTILS_LOG_LEVEL_LOCAL(level, format, ...) do { \ + if (level >= ESP_UTILS_LOG_GLOBAL_LEVEL) ESP_UTILS_LOG_LEVEL(level, format, ##__VA_ARGS__); \ + } while(0) + +/** + * Macros to simplify logging calls + * + */ +#if ESP_UTILS_ENABLE_LOG +#define ESP_UTILS_LOGD(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGI(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_INFO, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGW(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_WARNING, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGE(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_ERROR, format, ##__VA_ARGS__) +#else +#define ESP_UTILS_LOGD(...) +#define ESP_UTILS_LOGI(...) +#define ESP_UTILS_LOGW(...) +#define ESP_UTILS_LOGE(...) +#endif // ESP_UTILS_ENABLE_LOG + +#endif // __cplusplus + +/** + * Micros to log trace of function calls + * + */ +#if ESP_UTILS_ENABLE_LOG_TRACE +#define ESP_UTILS_LOG_TRACE_ENTER() ESP_UTILS_LOGD("Enter") +#define ESP_UTILS_LOG_TRACE_EXIT() ESP_UTILS_LOGD("Exit") +#else +#define ESP_UTILS_LOG_TRACE_ENTER() +#define ESP_UTILS_LOG_TRACE_EXIT() +#endif + +/** + * Macros to replace ESP-IDF logging functions + * + */ +#undef ESP_LOGV +#undef ESP_LOGD +#undef ESP_LOGI +#undef ESP_LOGW +#undef ESP_LOGE +#define ESP_LOGV(TAG, ...) { (void)TAG; ESP_UTILS_LOGD(__VA_ARGS__); } +#define ESP_LOGD(TAG, ...) { (void)TAG; ESP_UTILS_LOGD(__VA_ARGS__); } +#define ESP_LOGI(TAG, ...) { (void)TAG; ESP_UTILS_LOGI(__VA_ARGS__); } +#define ESP_LOGW(TAG, ...) { (void)TAG; ESP_UTILS_LOGW(__VA_ARGS__); } +#define ESP_LOGE(TAG, ...) { (void)TAG; ESP_UTILS_LOGE(__VA_ARGS__); } diff --git a/src/memory/esp_utils_memory.c b/src/memory/esp_utils_memory.c new file mode 100644 index 0000000..2bdc3ec --- /dev/null +++ b/src/memory/esp_utils_memory.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "esp_utils_config_internal.h" +#include "check/esp_utils_check.h" +#include "log/esp_utils_log.h" +#if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_ESP +#include "esp_heap_caps.h" +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_GENERAL_ALLOC_TYPE_CUSTOM +#include ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_INCLUDE +#endif + +void *esp_utils_memory_malloc(size_t size) +{ + void *p = NULL; +#if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_MICROPYTHON +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_ESP + p = heap_caps_malloc(size, ESP_UTILS_MEM_GENERAL_ALLOC_ESP_CAPS); +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM + p = ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_MALLOC(size); +#else + p = malloc(size); +#endif // ESP_UTILS_MEM_GENERAL_ALLOC_TYPE + ESP_UTILS_LOGD("Malloc @%p: %d", p, (int)size); + return p; +} + +void esp_utils_memory_free(void *p) +{ + ESP_UTILS_LOGD("Free @%p", p); +#if ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_MICROPYTHON +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_ESP + heap_caps_free(p); +#elif ESP_UTILS_MEM_GENERAL_ALLOC_TYPE == ESP_UTILS_MEM_ALLOC_TYPE_CUSTOM + ESP_UTILS_MEM_GENERAL_ALLOC_CUSTOM_FREE(p); +#else + free(p); +#endif // ESP_UTILS_MEM_GENERAL_ALLOC_TYPE +} diff --git a/src/memory/esp_utils_memory.h b/src/memory/esp_utils_memory.h new file mode 100644 index 0000000..a7a868e --- /dev/null +++ b/src/memory/esp_utils_memory.h @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sdkconfig.h" +#include "esp_utils_config_internal.h" + +#ifdef __cplusplus + +#include +#include + +extern "C" void *esp_utils_memory_malloc(size_t size); +extern "C" void esp_utils_memory_free(void *p); + +namespace esp_utils { + +template +struct MemoryAllocator { + using value_type = T; + + MemoryAllocator() = default; + + template + MemoryAllocator(const MemoryAllocator &) {} + + T *allocate(std::size_t n) + { + if (n == 0) { + return nullptr; + } + void *ptr = esp_utils_memory_malloc(n * sizeof(T)); +#if CONFIG_COMPILER_CXX_EXCEPTIONS + if (ptr == nullptr) { + throw std::bad_alloc(); + } +#endif // CONFIG_COMPILER_CXX_EXCEPTIONS + return static_cast(ptr); + } + + void deallocate(T *p, std::size_t n) + { + esp_utils_memory_free(p); + } + + template + void construct(U *p, Args &&... args) + { + new (p) U(std::forward(args)...); + } + + template + void destroy(U *p) + { + p->~U(); + } +}; + +/** + * @brief Helper function to create a shared pointer using the memory allocator + * + */ +template +std::shared_ptr make_shared(Args &&... args) +{ + return std::allocate_shared>(MemoryAllocator(), std::forward(args)...); +} + +} // namespace esp_utils + +#else + +void *esp_utils_memory_malloc(size_t size); +void esp_utils_memory_free(void *p); + +#endif // __cplusplus + +/** + * @brief Helper functions to allocate memory using the memory allocator + * + */ +#undef malloc +#undef free +#undef calloc +#define malloc(size) esp_utils_memory_malloc(size) +#define free(ptr) esp_utils_memory_free(ptr) +#define calloc(n, size) \ + ({ \ + size_t _size = (size_t)n * size; \ + void *p = malloc(_size); \ + if (p != NULL) { \ + memset(p, 0, _size); \ + } \ + p; \ + }) diff --git a/test_apps/CMakeLists.txt b/test_apps/CMakeLists.txt new file mode 100644 index 0000000..112dd03 --- /dev/null +++ b/test_apps/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +set(COMPONENTS main) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(lib_utils_test) diff --git a/test_apps/main/CMakeLists.txt b/test_apps/main/CMakeLists.txt new file mode 100644 index 0000000..e425c85 --- /dev/null +++ b/test_apps/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "test_app_main.cpp" "test_on_c.c" "test_on_cpp.cpp" + WHOLE_ARCHIVE +) diff --git a/test_apps/main/idf_component.yml b/test_apps/main/idf_component.yml new file mode 100644 index 0000000..06cfa10 --- /dev/null +++ b/test_apps/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + test_utils: + path: ${IDF_PATH}/tools/unit-test-app/components/test_utils + esp-lib-utils: + version: "*" + override_path: "../../../esp-lib-utils" diff --git a/test_apps/main/test_app_main.cpp b/test_apps/main/test_app_main.cpp new file mode 100644 index 0000000..31d6951 --- /dev/null +++ b/test_apps/main/test_app_main.cpp @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "unity.h" +#include "unity_test_utils.h" + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (400) + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + esp_reent_cleanup(); //clean up some of the newlib's lazy allocations + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} +#else +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = before_free - after_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta < TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} +#endif // ESP_IDF_VERSION + +extern "C" void app_main(void) +{ + /** + * __ ______ _______ __ __ ________ ______ __ ______ + * | \ | \| \ | \ | \| \| \| \ / \ + * | $$ \$$$$$$| $$$$$$$\ | $$ | $$ \$$$$$$$$ \$$$$$$| $$ | $$$$$$\ + * | $$ | $$ | $$__/ $$ | $$ | $$ | $$ | $$ | $$ | $$___\$$ + * | $$ | $$ | $$ $$ | $$ | $$ | $$ | $$ | $$ \$$ \ + * | $$ | $$ | $$$$$$$\ | $$ | $$ | $$ | $$ | $$ _\$$$$$$\ + * | $$_____ _| $$_ | $$__/ $$ | $$__/ $$ | $$ _| $$_ | $$_____| \__| $$ + * | $$ \| $$ \| $$ $$ \$$ $$ | $$ | $$ \| $$ \\$$ $$ + * \$$$$$$$$ \$$$$$$ \$$$$$$$ \$$$$$$ \$$ \$$$$$$ \$$$$$$$$ \$$$$$$ + */ + printf(" __ ______ _______ __ __ ________ ______ __ ______\r\n"); + printf("| \\ | \\| \\ | \\ | \\| \\| \\| \\ / \\\r\n"); + printf("| $$ \\$$$$$$| $$$$$$$\\ | $$ | $$ \\$$$$$$$$ \\$$$$$$| $$ | $$$$$$\\\r\n"); + printf("| $$ | $$ | $$__/ $$ | $$ | $$ | $$ | $$ | $$ | $$___\\$$\r\n"); + printf("| $$ | $$ | $$ $$ | $$ | $$ | $$ | $$ | $$ \\$$ \\\r\n"); + printf("| $$ | $$ | $$$$$$$\\ | $$ | $$ | $$ | $$ | $$ _\\$$$$$$\\\r\n"); + printf("| $$_____ _| $$_ | $$__/ $$ | $$__/ $$ | $$ _| $$_ | $$_____| \\__| $$\r\n"); + printf("| $$ \\| $$ \\| $$ $$ \\$$ $$ | $$ | $$ \\| $$ \\\\$$ $$\r\n"); + printf(" \\$$$$$$$$ \\$$$$$$ \\$$$$$$$ \\$$$$$$ \\$$ \\$$$$$$ \\$$$$$$$$ \\$$$$$$\r\n"); + unity_run_menu(); +} diff --git a/test_apps/main/test_on_c.c b/test_apps/main/test_on_c.c new file mode 100644 index 0000000..80f012e --- /dev/null +++ b/test_apps/main/test_on_c.c @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ +#include "unity.h" +#define ESP_UTILS_LOG_TAG "TestC" +#include "esp_utils_library.h" + +TEST_CASE("Test log functions on C", "[utils][log][C]") +{ + ESP_UTILS_LOG_TRACE_ENTER(); + + ESP_UTILS_LOGD("This is a debug message"); + ESP_UTILS_LOGI("This is an info message"); + ESP_UTILS_LOGW("This is a warning message"); + ESP_UTILS_LOGE("This is an error message"); + + ESP_UTILS_LOG_TRACE_EXIT(); +} + +#define MALLOC_GOOD_SIZE (1 * 1024) +#define MALLOC_BAD_SIZE (1 * 1024 * 1024) + +TEST_CASE("Test memory functions on C", "[utils][memory][C]") +{ + char *good_ptr = (char *)calloc(1, MALLOC_GOOD_SIZE); + ESP_UTILS_CHECK_NULL_GOTO(good_ptr, err, "Failed to allocate memory size: %d", MALLOC_GOOD_SIZE); + ESP_UTILS_LOGI("Malloced value: %d", good_ptr[0]); + + char *bad_ptr = (char *)calloc(1, MALLOC_BAD_SIZE); + ESP_UTILS_CHECK_NULL_GOTO(bad_ptr, end, "Failed to allocate memory size: %d", MALLOC_BAD_SIZE); + ESP_UTILS_LOGI("Malloced value: %d", bad_ptr[0]); + +err: + TEST_ASSERT(false && "Memory allocation failed"); + +end: + free(good_ptr); + + return; +} + +static bool test_check_false_return(void) +{ + ESP_UTILS_CHECK_FALSE_RETURN(true, false, "Check false return failed"); + ESP_UTILS_CHECK_FALSE_RETURN(false, true, "Check false return success"); + + TEST_ASSERT(false && "Check false return failed"); + + return false; +} + +static bool test_check_false_goto(void) +{ + ESP_UTILS_CHECK_FALSE_GOTO(true, err, "Check false goto failed"); + ESP_UTILS_CHECK_FALSE_GOTO(false, end, "Check false goto success"); + +err: + TEST_ASSERT(false && "Check false goto failed"); + + return false; + +end: + return true; +} + +static bool test_check_false_exit_result = false; +static void test_check_false_exit(void) +{ + ESP_UTILS_CHECK_FALSE_EXIT(true, "Check false exit failed"); + test_check_false_exit_result = true; + ESP_UTILS_CHECK_FALSE_EXIT(false, "Check false exit success"); + + TEST_ASSERT(false && "Check false exit failed"); +} + +static bool test_check_error_return(void) +{ + ESP_UTILS_CHECK_ERROR_RETURN(ESP_OK, false, "Check error return failed"); + ESP_UTILS_CHECK_ERROR_RETURN(ESP_FAIL, true, "Check error return success"); + + TEST_ASSERT(false && "Check error return failed"); + + return false; +} + +static bool test_check_error_goto(void) +{ + ESP_UTILS_CHECK_ERROR_GOTO(ESP_OK, err, "Check error goto failed"); + ESP_UTILS_CHECK_ERROR_GOTO(ESP_FAIL, end, "Check error goto success"); + +err: + TEST_ASSERT(false && "Check error goto failed"); + + return false; + +end: + return true; +} + +static bool test_check_error_exit_result = false; +static void test_check_error_exit(void) +{ + ESP_UTILS_CHECK_ERROR_EXIT(ESP_OK, "Check error exit failed"); + test_check_error_exit_result = true; + ESP_UTILS_CHECK_ERROR_EXIT(ESP_FAIL, "Check error exit success"); + + TEST_ASSERT(false && "Check error exit failed"); +} + +static bool test_check_null_return(void) +{ + ESP_UTILS_CHECK_NULL_RETURN((void *)1, false, "Check null return failed"); + ESP_UTILS_CHECK_NULL_RETURN(NULL, true, "Check null return success"); + + TEST_ASSERT(false && "Check null return failed"); + + return false; +} + +static bool test_check_null_goto(void) +{ + ESP_UTILS_CHECK_NULL_GOTO((void *)1, err, "Check null goto failed"); + ESP_UTILS_CHECK_NULL_GOTO(NULL, end, "Check null goto success"); + +err: + TEST_ASSERT(false && "Check null goto failed"); + + return false; + +end: + return true; +} + +bool test_check_null_exit_result = false; +static void test_check_null_exit(void) +{ + ESP_UTILS_CHECK_NULL_EXIT((void *)1, "Check null exit failed"); + test_check_null_exit_result = true; + ESP_UTILS_CHECK_NULL_EXIT(NULL, "Check null exit success"); + + TEST_ASSERT(false && "Check null exit failed"); +} + +TEST_CASE("Test check functions on cpp", "[utils][check][C]") +{ + test_check_false_return(); + test_check_false_goto(); + test_check_false_exit(); + TEST_ASSERT(test_check_false_exit_result); + test_check_error_return(); + test_check_error_goto(); + test_check_error_exit(); + TEST_ASSERT(test_check_error_exit_result); + test_check_null_return(); + test_check_null_goto(); + test_check_null_exit(); + TEST_ASSERT(test_check_null_exit_result); +} diff --git a/test_apps/main/test_on_cpp.cpp b/test_apps/main/test_on_cpp.cpp new file mode 100644 index 0000000..27d614e --- /dev/null +++ b/test_apps/main/test_on_cpp.cpp @@ -0,0 +1,175 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ +#include +#include "unity.h" +#define ESP_UTILS_LOG_TAG "TestCpp" +#include "esp_utils_library.h" + +TEST_CASE("Test log functions on cpp", "[utils][log][CPP]") +{ + ESP_UTILS_LOG_TRACE_ENTER(); + + ESP_UTILS_LOGD("This is a debug message"); + ESP_UTILS_LOGI("This is an info message"); + ESP_UTILS_LOGW("This is a warning message"); + ESP_UTILS_LOGE("This is an error message"); + + ESP_UTILS_LOG_TRACE_EXIT(); +} + +#define MALLOC_GOOD_SIZE (1 * 1024) +#define MALLOC_BAD_SIZE (1 * 1024 * 1024) + +template +class TestClass { +public: + char buffer[N] = { 128 }; +}; + +using TestGoodClass = TestClass; +using TestBadClass = TestClass; + +TEST_CASE("Test memory functions on cpp", "[utils][memory][CPP]") +{ + std::shared_ptr good_ptr = nullptr; + ESP_UTILS_CHECK_EXCEPTION_GOTO( + (good_ptr = esp_utils::make_shared()), err, "Failed to allocate memory size: %d", + MALLOC_GOOD_SIZE + ); + ESP_UTILS_LOGI("Malloced value: %d", good_ptr->buffer[0]); + + { + std::shared_ptr bad_ptr = nullptr; + ESP_UTILS_CHECK_EXCEPTION_GOTO( + (bad_ptr = esp_utils::make_shared()), end, "Failed to allocate memory size: %d", MALLOC_BAD_SIZE + ); + ESP_UTILS_LOGI("Malloced value: %d", bad_ptr->buffer[0]); + } + +err: + TEST_ASSERT(false && "Memory allocation failed"); + +end: + return; +} + +static bool test_check_false_return(void) +{ + ESP_UTILS_CHECK_FALSE_RETURN(true, false, "Check false return failed"); + ESP_UTILS_CHECK_FALSE_RETURN(false, true, "Check false return success"); + + TEST_ASSERT(false && "Check false return failed"); + + return false; +} + +static bool test_check_false_goto(void) +{ + ESP_UTILS_CHECK_FALSE_GOTO(true, err, "Check false goto failed"); + ESP_UTILS_CHECK_FALSE_GOTO(false, end, "Check false goto success"); + +err: + TEST_ASSERT(false && "Check false goto failed"); + + return false; + +end: + return true; +} + +static bool test_check_false_exit_result = false; +static void test_check_false_exit(void) +{ + ESP_UTILS_CHECK_FALSE_EXIT(true, "Check false exit failed"); + test_check_false_exit_result = true; + ESP_UTILS_CHECK_FALSE_EXIT(false, "Check false exit success"); + + TEST_ASSERT(false && "Check false exit failed"); +} + +static bool test_check_error_return(void) +{ + ESP_UTILS_CHECK_ERROR_RETURN(ESP_OK, false, "Check error return failed"); + ESP_UTILS_CHECK_ERROR_RETURN(ESP_FAIL, true, "Check error return success"); + + TEST_ASSERT(false && "Check error return failed"); + + return false; +} + +static bool test_check_error_goto(void) +{ + ESP_UTILS_CHECK_ERROR_GOTO(ESP_OK, err, "Check error goto failed"); + ESP_UTILS_CHECK_ERROR_GOTO(ESP_FAIL, end, "Check error goto success"); + +err: + TEST_ASSERT(false && "Check error goto failed"); + + return false; + +end: + return true; +} + +static bool test_check_error_exit_result = false; +static void test_check_error_exit(void) +{ + ESP_UTILS_CHECK_ERROR_EXIT(ESP_OK, "Check error exit failed"); + test_check_error_exit_result = true; + ESP_UTILS_CHECK_ERROR_EXIT(ESP_FAIL, "Check error exit success"); + + TEST_ASSERT(false && "Check error exit failed"); +} + +static bool test_check_null_return(void) +{ + ESP_UTILS_CHECK_NULL_RETURN((void *)1, false, "Check null return failed"); + ESP_UTILS_CHECK_NULL_RETURN(NULL, true, "Check null return success"); + + TEST_ASSERT(false && "Check null return failed"); + + return false; +} + +static bool test_check_null_goto(void) +{ + ESP_UTILS_CHECK_NULL_GOTO((void *)1, err, "Check null goto failed"); + ESP_UTILS_CHECK_NULL_GOTO(NULL, end, "Check null goto success"); + +err: + TEST_ASSERT(false && "Check null goto failed"); + + return false; + +end: + return true; +} + +static bool test_check_null_exit_result = false; +static void test_check_null_exit(void) +{ + ESP_UTILS_CHECK_NULL_EXIT((void *)1, "Check null exit failed"); + test_check_null_exit_result = true; + ESP_UTILS_CHECK_NULL_EXIT(NULL, "Check null exit success"); + + TEST_ASSERT(false && "Check null exit failed"); +} + +TEST_CASE("Test check functions on cpp", "[utils][check][CPP]") +{ + test_check_false_return(); + test_check_false_goto(); + test_check_false_exit(); + TEST_ASSERT(test_check_false_exit_result); + test_check_error_return(); + test_check_error_goto(); + test_check_error_exit(); + TEST_ASSERT(test_check_error_exit_result); + test_check_null_return(); + test_check_null_goto(); + test_check_null_exit(); + TEST_ASSERT(test_check_null_exit_result); +} diff --git a/test_apps/sdkconfig.ci.cxx_exceptions b/test_apps/sdkconfig.ci.cxx_exceptions new file mode 100644 index 0000000..75ebeae --- /dev/null +++ b/test_apps/sdkconfig.ci.cxx_exceptions @@ -0,0 +1 @@ +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/test_apps/sdkconfig.defaults b/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..4e51191 --- /dev/null +++ b/test_apps/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_COMPILER_CXX_EXCEPTIONS=y + +CONFIG_ESP_UTILS_LOG_LEVEL_DEBUG=y +CONFIG_ESP_UTILS_ENABLE_LOG_TRACE=y diff --git a/tools/check_executables.py b/tools/check_executables.py new file mode 100755 index 0000000..d959fa5 --- /dev/null +++ b/tools/check_executables.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import os +import sys +from typing import Iterable, List + +try: + from idf_ci_utils import is_executable +except ImportError: + sys.path.append(os.path.join(os.path.dirname(__file__))) + + from idf_ci_utils import is_executable + + +def _strip_each_item(iterable: Iterable) -> List: + res = [] + for item in iterable: + if item: + res.append(item.strip()) + return res + + +COMPONENT_PATH = os.getenv('COMPONENT_PATH', os.getcwd()) +EXECUTABLE_LIST_FN = os.path.join(COMPONENT_PATH, 'tools/executable-list.txt') +known_executables = _strip_each_item(open(EXECUTABLE_LIST_FN).readlines()) + + +def check_executable_list() -> int: + ret = 0 + for index, fn in enumerate(known_executables): + if not os.path.exists(os.path.join(COMPONENT_PATH, fn)): + print('{}:{} {} not exists. Please remove it manually'.format(EXECUTABLE_LIST_FN, index + 1, fn)) + ret = 1 + return ret + + +def check_executables(files: List) -> int: + ret = 0 + for fn in files: + fn_executable = is_executable(fn) + fn_in_list = fn in known_executables + if fn_executable and not fn_in_list: + print('"{}" is not in {}'.format(fn, EXECUTABLE_LIST_FN)) + ret = 1 + if not fn_executable and fn_in_list: + print('"{}" is not executable but is in {}'.format(fn, EXECUTABLE_LIST_FN)) + ret = 1 + return ret + + +def check() -> int: + parser = argparse.ArgumentParser() + parser.add_argument('--action', choices=['executables', 'list'], required=True, + help='if "executables", pass all your executables to see if it\'s in the list.' + 'if "list", check if all items on your list exist') + parser.add_argument('filenames', nargs='*', help='Filenames to check.') + args = parser.parse_args() + + if args.action == 'executables': + ret = check_executables(args.filenames) + elif args.action == 'list': + ret = check_executable_list() + else: + raise ValueError + + return ret + + +if __name__ == '__main__': + sys.exit(check()) diff --git a/tools/check_file_version.py b/tools/check_file_version.py new file mode 100644 index 0000000..60cad84 --- /dev/null +++ b/tools/check_file_version.py @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import re + +internal_version_file = 'src/esp_utils_versions.h' +internal_version_macross = [ + { + 'file': 'library.properties', + 'macro': { + 'major': 'ESP_UTILS_VERSION_MAJOR', + 'minor': 'ESP_UTILS_VERSION_MINOR', + 'patch': 'ESP_UTILS_VERSION_PATCH' + }, + }, + { + 'file': 'esp_utils_config.h', + 'macro': { + 'major': 'ESP_UTILS_CONFIG_VERSION_MAJOR', + 'minor': 'ESP_UTILS_CONFIG_VERSION_MINOR', + 'patch': 'ESP_UTILS_CONFIG_VERSION_PATCH' + }, + }, +] +file_version_macros = [ + { + 'file': 'esp_utils_config.h', + 'macro': { + 'major': 'ESP_UTILS_CONFIG_FILE_VERSION_MAJOR', + 'minor': 'ESP_UTILS_CONFIG_FILE_VERSION_MINOR', + 'patch': 'ESP_UTILS_CONFIG_FILE_VERSION_PATCH' + }, + }, +] +arduino_version_file = 'library.properties' + + +def extract_file_version(file_path, version_dict): + file_contents = [] + content_str = '' + with open(file_path, 'r') as file: + file_contents.append(file.readlines()) + for content in file_contents: + content_str = ''.join(content) + + version_macro = version_dict['macro'] + major_version = re.search(r'#define ' + version_macro['major'] + r' (\d+)', content_str) + minor_version = re.search(r'#define ' + version_macro['minor'] + r' (\d+)', content_str) + patch_version = re.search(r'#define ' + version_macro['patch'] + r' (\d+)', content_str) + + if major_version and minor_version and patch_version: + return {'file': version_dict['file'], 'version': major_version.group(1) + '.' + minor_version.group(1) + '.' + patch_version.group(1)} + + return None + + +def extract_arduino_version(file_path): + file_contents = [] + content_str = '' + with open(file_path, 'r') as file: + file_contents.append(file.readlines()) + for content in file_contents: + content_str = ''.join(content) + + version = re.search(r'version=(\d+\.\d+\.\d+)', content_str) + + if version: + return {'file': os.path.basename(file_path), 'version': version.group(1)} + + return None + + +if __name__ == '__main__': + if len(sys.argv) >= 3: + search_directory = sys.argv[1] + + # Extract internal versions + internal_version_path = os.path.join(search_directory, internal_version_file) + internal_versions = [] + print(f"Internal version extracted from '{internal_version_path}") + for internal_version_macros in internal_version_macross: + version = extract_file_version(internal_version_path, internal_version_macros) + if version: + print(f"Internal version: '{internal_version_macros['file']}': {version}") + internal_versions.append(version) + else: + print(f"'{internal_version_macros['file']}' version not found") + sys.exit(1) + + # Check file versions + for i in range(2, len(sys.argv)): + file_path = sys.argv[i] + + if file_path in internal_version_file: + print(f"Skipping '{file_path}'") + continue + + src_file = os.path.join(search_directory, os.path.basename(file_path)) + for file_version in file_version_macros: + versions = extract_file_version(src_file, file_version) + if versions: + print(f"File version extracted from '{src_file}': {versions}") + for internal_version in internal_versions: + if (internal_version['file'] == versions['file']) and (internal_version['version'] != versions['version']): + print(f"Version mismatch: '{internal_version['file']}'") + sys.exit(1) + print(f'Version matched') + + # Extract arduino version + arduino_version_path = os.path.join(search_directory, arduino_version_file) + arduino_version = extract_arduino_version(arduino_version_path) + print(f"Arduino version extracted from '{arduino_version_path}") + if arduino_version: + print(f'Arduino version: {arduino_version}') + else: + print(f'Arduino version not found') + + # Check arduino version + for internal_version in internal_versions: + if (internal_version['file'] == arduino_version_file) and (internal_version['version'] != arduino_version['version']): + print(f"Arduino version mismatch: '{internal_version['file']}'") + sys.exit(1) + print(f'Arduino Version matched') diff --git a/tools/executable-list.txt b/tools/executable-list.txt new file mode 100644 index 0000000..d44a038 --- /dev/null +++ b/tools/executable-list.txt @@ -0,0 +1,2 @@ +.github/scripts/check_lib_versions.sh +tools/check_executables.py diff --git a/tools/idf_ci_utils.py b/tools/idf_ci_utils.py new file mode 100644 index 0000000..3e541fa --- /dev/null +++ b/tools/idf_ci_utils.py @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# internal use only for CI +# some CI related util functions + +import logging +import os +import subprocess +import sys +from typing import Any, List + +IDF_PATH = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..'))) + + +def get_submodule_dirs(full_path: bool = False) -> List[str]: + """ + To avoid issue could be introduced by multi-os or additional dependency, + we use python and git to get this output + :return: List of submodule dirs + """ + dirs = [] + try: + lines = ( + subprocess.check_output( + [ + 'git', + 'config', + '--file', + os.path.realpath(os.path.join(IDF_PATH, '.gitmodules')), + '--get-regexp', + 'path', + ] + ) + .decode('utf8') + .strip() + .split('\n') + ) + for line in lines: + _, path = line.split(' ') + if full_path: + dirs.append(os.path.join(IDF_PATH, path)) + else: + dirs.append(path) + except Exception as e: # pylint: disable=W0703 + logging.warning(str(e)) + + return dirs + + +def _check_git_filemode(full_path: str) -> bool: + try: + stdout = subprocess.check_output(['git', 'ls-files', '--stage', full_path]).strip().decode('utf-8') + except subprocess.CalledProcessError: + return True + + mode = stdout.split(' ', 1)[0] # e.g. 100644 for a rw-r--r-- + if any([int(i, 8) & 1 for i in mode[-3:]]): + return True + return False + + +def is_executable(full_path: str) -> bool: + """ + os.X_OK will always return true on windows. Use git to check file mode. + :param full_path: file full path + :return: True is it's an executable file + """ + if sys.platform == 'win32': + return _check_git_filemode(full_path) + return os.access(full_path, os.X_OK) + + +def get_git_files(path: str = IDF_PATH, full_path: bool = False) -> List[str]: + """ + Get the result of git ls-files + :param path: path to run git ls-files + :param full_path: return full path if set to True + :return: list of file paths + """ + try: + # this is a workaround when using under worktree + # if you're using worktree, when running git commit a new environment variable GIT_DIR would be declared, + # the value should be /.git/worktrees/ + # This would affect the return value of `git ls-files`, unset this would use the `cwd`value or its parent + # folder if no `.git` folder found in `cwd`. + workaround_env = os.environ.copy() + workaround_env.pop('GIT_DIR', None) + files = ( + subprocess.check_output(['git', 'ls-files'], cwd=path, env=workaround_env) + .decode('utf8') + .strip() + .split('\n') + ) + except Exception as e: # pylint: disable=W0703 + logging.warning(str(e)) + files = [] + return [os.path.join(path, f) for f in files] if full_path else files + + +def is_in_directory(file_path: str, folder: str) -> bool: + return os.path.realpath(file_path).startswith(os.path.realpath(folder) + os.sep) + + +def to_list(s: Any) -> List[Any]: + if isinstance(s, (set, tuple)): + return list(s) + + if isinstance(s, list): + return s + + return [s]