diff --git a/CMakeLists.txt b/CMakeLists.txt index 226a67d5e..fcf70d9aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,8 +74,8 @@ idf_get_git_revision() # Check that the targets set in cache, sdkconfig, and in environment all match idf_check_config_target() -## if project uses git, retrieve revision -git_describe(PROJECT_VER "${CMAKE_CURRENT_SOURCE_DIR}") +## get PROJECT_VER +app_get_revision("${CMAKE_SOURCE_DIR}") # Add some idf-wide definitions idf_set_global_compile_options() diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index e559ef102..2837244ad 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -1,11 +1,16 @@ -set(COMPONENT_SRCS "esp_ota_ops.c") +set(COMPONENT_SRCS "esp_ota_ops.c" + "esp_app_desc.c") set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_REQUIRES spi_flash partition_table) -set(COMPONENT_PRIV_REQUIRES bootloader_support) +set(COMPONENT_REQUIRES spi_flash partition_table bootloader_support) register_component() +set_source_files_properties( + SOURCE "esp_app_desc.c" + PROPERTIES COMPILE_DEFINITIONS + PROJECT_VER=\"${PROJECT_VER}\") + # Add custom target for generating empty otadata partition for flashing if(OTADATA_PARTITION_OFFSET AND OTADATA_PARTITION_SIZE) add_custom_command(OUTPUT "${IDF_BUILD_ARTIFACTS_DIR}/${BLANK_OTADATA_FILE}" diff --git a/components/app_update/Kconfig.projbuild b/components/app_update/Kconfig.projbuild new file mode 100644 index 000000000..62f8bdec3 --- /dev/null +++ b/components/app_update/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "Application manager" + +config APP_COMPILE_TIME_DATE + bool "Use time/date stamp for app" + default y + help + If set, then the app will be built with the current time/date stamp. It is stored in the app description structure. + If not set, time/date stamp will be excluded from app image. + This can be useful for getting the same binary image files made from the same source, + but at different times. + +endmenu # "Application manager" \ No newline at end of file diff --git a/components/app_update/Makefile.projbuild b/components/app_update/Makefile.projbuild index c9ee59137..ebae19fee 100644 --- a/components/app_update/Makefile.projbuild +++ b/components/app_update/Makefile.projbuild @@ -34,5 +34,9 @@ erase_ota: erase_otadata all: blank_ota_data flash: blank_ota_data +TMP_DEFINES := $(BUILD_DIR_BASE)/app_update/tmp_cppflags.txt +export TMP_DEFINES + clean: - rm -f $(BLANK_OTA_DATA_FILE) \ No newline at end of file + rm -f $(BLANK_OTA_DATA_FILE) + rm -f $(TMP_DEFINES) diff --git a/components/app_update/component.mk b/components/app_update/component.mk index c2c4c03a1..924831e38 100644 --- a/components/app_update/component.mk +++ b/components/app_update/component.mk @@ -3,3 +3,47 @@ # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) +# esp_app_desc structure is added as an undefined symbol because otherwise the +# linker will ignore this structure as it has no other files depending on it. +COMPONENT_ADD_LDFLAGS += -u esp_app_desc + +ifndef IS_BOOTLOADER_BUILD +GET_PROJECT_VER ?= +ifeq ("${PROJECT_VER}", "") +ifeq ("$(wildcard ${PROJECT_PATH}/version.txt)","") +GET_PROJECT_VER := $(shell cd ${PROJECT_PATH} && git describe --always --tags --dirty || echo "Not found git repo") +ifeq ("${GET_PROJECT_VER}", "Not found git repo") +$(info Project do not have git repo, it needs to get PROJECT_VER from `git describe` command.) +GET_PROJECT_VER := "" +endif +else +# read from version.txt +GET_PROJECT_VER := $(shell cat ${PROJECT_PATH}/version.txt) +endif +endif +# If ``PROJECT_VER`` variable set in project Makefile file, its value will be used. +# Else, if the ``$PROJECT_PATH/version.txt`` exists, its contents will be used as ``PROJECT_VER``. +# Else, if the project is located inside a Git repository, the output of git describe will be used. +# Otherwise, ``PROJECT_VER`` will be empty. + +ifeq ("${PROJECT_VER}", "") +PROJECT_VER:= $(GET_PROJECT_VER) +else +PROJECT_VER:= $(PROJECT_VER) +endif +$(info App "$(PROJECT_NAME)" version: $(PROJECT_VER)) + +NEW_DEFINES:= $(PROJECT_VER) $(PROJECT_NAME) $(IDF_VER) +ifeq ("$(wildcard ${TMP_DEFINES})","") +OLD_DEFINES:= +else +OLD_DEFINES:= $(shell cat $(TMP_DEFINES)) +endif + +# If NEW_DEFINES (PROJECT_VER, PROJECT_NAME) were changed then rebuild only esp_app_desc. +ifneq ("${NEW_DEFINES}", "${OLD_DEFINES}") +$(shell echo $(NEW_DEFINES) > $(TMP_DEFINES); rm -f esp_app_desc.o;) +endif + +esp_app_desc.o: CPPFLAGS += -D PROJECT_VER=\"$(PROJECT_VER)\" -D PROJECT_NAME=\"$(PROJECT_NAME)\" +endif diff --git a/components/app_update/esp_app_desc.c b/components/app_update/esp_app_desc.c new file mode 100644 index 000000000..b120d2c34 --- /dev/null +++ b/components/app_update/esp_app_desc.c @@ -0,0 +1,49 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_ota_ops.h" +#include "sdkconfig.h" + +// Application version info +const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = { + .magic_word = ESP_APP_DESC_MAGIC_WORD, + .version = PROJECT_VER, + .project_name = PROJECT_NAME, + .idf_ver = IDF_VER, + +#ifdef CONFIG_APP_SECURE_VERSION + .secure_version = CONFIG_APP_SECURE_VERSION, +#else + .secure_version = 0, +#endif + +#ifdef CONFIG_APP_COMPILE_TIME_DATE + .time = __TIME__, + .date = __DATE__, +#else + .time = "", + .date = "", +#endif +}; + + +_Static_assert(sizeof(PROJECT_VER) <= sizeof(esp_app_desc.version), "PROJECT_VER is longer than version field in structure"); +_Static_assert(sizeof(IDF_VER) <= sizeof(esp_app_desc.idf_ver), "IDF_VER is longer than idf_ver field in structure"); +_Static_assert(sizeof(PROJECT_NAME) <= sizeof(esp_app_desc.project_name), "PROJECT_NAME is longer than project_name field in structure"); + +const esp_app_desc_t *esp_ota_get_app_description(void) +{ + return &esp_app_desc; +} diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index 6416b5d93..fdab1b95c 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -576,3 +576,25 @@ const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t * return default_ota; } + +esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc) +{ + if (partition == NULL || app_desc == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if(partition->type != ESP_PARTITION_TYPE_APP) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = esp_partition_read(partition, sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), app_desc, sizeof(esp_app_desc_t)); + if (err != ESP_OK) { + return err; + } + + if (app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index ca77b5422..3a2355d9e 100644 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -20,6 +20,7 @@ #include #include "esp_err.h" #include "esp_partition.h" +#include "esp_image_format.h" #ifdef __cplusplus extern "C" @@ -41,6 +42,14 @@ extern "C" */ typedef uint32_t esp_ota_handle_t; +/** + * @brief Return esp_app_desc structure. This structure includes app version. + * + * Return description for running app. + * @return Pointer to esp_app_desc structure. + */ +const esp_app_desc_t *esp_ota_get_app_description(void); + /** * @brief Commence an OTA update writing to the specified partition. @@ -170,6 +179,22 @@ const esp_partition_t* esp_ota_get_running_partition(void); */ const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from); +/** + * @brief Returns esp_app_desc structure for app partition. This structure includes app version. + * + * Returns a description for the requested app partition. + * @param[in] partition Pointer to app partition. (only app partition) + * @param[out] app_desc Structure of info about app. + * @return + * - ESP_OK Successful. + * - ESP_ERR_NOT_FOUND app_desc structure is not found. Magic word is incorrect. + * - ESP_ERR_NOT_SUPPORTED Partition is not application. + * - ESP_ERR_INVALID_ARG Arguments is NULL or if partition's offset exceeds partition size. + * - ESP_ERR_INVALID_SIZE Read would go out of bounds of the partition. + * - or one of error codes from lower-level flash driver. + */ +esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc); + #ifdef __cplusplus } #endif diff --git a/components/app_update/test/test_ota_ops.c b/components/app_update/test/test_ota_ops.c index e0a7864b5..e7773ac24 100644 --- a/components/app_update/test/test_ota_ops.c +++ b/components/app_update/test/test_ota_ops.c @@ -8,7 +8,7 @@ #include #include #include - +#include "bootloader_common.h" /* These OTA tests currently don't assume an OTA partition exists on the device, so they're a bit limited @@ -84,3 +84,26 @@ TEST_CASE("esp_ota_get_next_update_partition logic", "[ota]") TEST_ASSERT_EQUAL_PTR(ota_0, p); } +TEST_CASE("esp_ota_get_partition_description ", "[ota]") +{ + const esp_partition_t *running = esp_ota_get_running_partition(); + TEST_ASSERT_NOT_NULL(running); + esp_app_desc_t app_desc1, app_desc2; + TEST_ESP_OK(esp_ota_get_partition_description(running, &app_desc1)); + const esp_partition_pos_t running_pos = { + .offset = running->address, + .size = running->size + }; + TEST_ESP_OK(bootloader_common_get_partition_description(&running_pos, &app_desc2)); + + TEST_ASSERT_EQUAL_MEMORY_MESSAGE((uint8_t *)&app_desc1, (uint8_t *)&app_desc2, sizeof(app_desc1), "must be the same"); + + const esp_partition_t *not_app = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL); + TEST_ASSERT_NOT_NULL(not_app); + TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ota_get_partition_description(not_app, &app_desc1)); + const esp_partition_pos_t not_app_pos = { + .offset = not_app->address, + .size = not_app->size + }; + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, bootloader_common_get_partition_description(¬_app_pos, &app_desc1)); +} diff --git a/components/bootloader_support/include/bootloader_common.h b/components/bootloader_support/include/bootloader_common.h index d5a92cc79..d15d902ec 100644 --- a/components/bootloader_support/include/bootloader_common.h +++ b/components/bootloader_support/include/bootloader_common.h @@ -14,6 +14,7 @@ #pragma once #include "esp_flash_data_types.h" +#include "esp_image_format.h" /// Type of hold a GPIO in low state typedef enum { @@ -91,3 +92,17 @@ bool bootloader_common_label_search(const char *list, char *label); * - ESP_FAIL: An allocation error occurred. */ esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256); + +/** + * @brief Returns esp_app_desc structure for app partition. This structure includes app version. + * + * Returns a description for the requested app partition. + * @param[in] partition App partition description. + * @param[out] app_desc Structure of info about app. + * @return + * - ESP_OK: Successful. + * - ESP_ERR_INVALID_ARG: The arguments passed are not valid. + * - ESP_ERR_NOT_FOUND: app_desc structure is not found. Magic word is incorrect. + * - ESP_FAIL: mapping is fail. + */ +esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc); diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index bce3b1d7f..7006cae98 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -89,6 +89,25 @@ typedef struct { uint32_t data_len; } esp_image_segment_header_t; +#define ESP_APP_DESC_MAGIC_WORD 0xABCD5432 /*!< The magic word for the esp_app_desc structure that is in DROM. */ + +/** + * @brief Description about application. + */ +typedef struct { + uint32_t magic_word; /*!< Magic word ESP_APP_DESC_MAGIC_WORD */ + uint32_t secure_version; /*!< Secure version */ + uint32_t reserv1[2]; /*!< --- */ + char version[32]; /*!< Application version */ + char project_name[32]; /*!< Project name */ + char time[16]; /*!< Compile time */ + char date[16]; /*!< Compile date*/ + char idf_ver[32]; /*!< Version IDF */ + uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */ + uint32_t reserv2[20]; /*!< --- */ +} esp_app_desc_t; +_Static_assert(sizeof(esp_app_desc_t) == 256, "esp_app_desc_t should be 256 bytes"); + #define ESP_IMAGE_MAX_SEGMENTS 16 /* Structure to hold on-flash image metadata */ diff --git a/components/bootloader_support/src/bootloader_common.c b/components/bootloader_support/src/bootloader_common.c index 479407845..c4feb32b7 100644 --- a/components/bootloader_support/src/bootloader_common.c +++ b/components/bootloader_support/src/bootloader_common.c @@ -192,3 +192,25 @@ esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t return ESP_OK; } + +esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc) +{ + if (partition == NULL || app_desc == NULL || partition->offset == 0) { + return ESP_ERR_INVALID_ARG; + } + + const uint8_t *image = bootloader_mmap(partition->offset, partition->size); + if (image == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", partition->offset, partition->size); + return ESP_FAIL; + } + + memcpy(app_desc, image + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), sizeof(esp_app_desc_t)); + bootloader_munmap(image); + + if (app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index d77aeb410..dfbd4b2c9 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -57,8 +57,9 @@ else() set(COMPONENT_REQUIRES driver tcpip_adapter esp_event) # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t # tcpip_adapter is a public requirement because esp_event.h uses tcpip_adapter types + # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. set(COMPONENT_PRIV_REQUIRES - app_trace bootloader_support ethernet log mbedtls nvs_flash + app_trace app_update bootloader_support ethernet log mbedtls nvs_flash pthread smartconfig_ack spi_flash vfs wpa_supplicant xtensa-debug-module) set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32_fragments.lf) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 34dc885e6..92ce879fe 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -71,6 +71,7 @@ #include "esp_pm.h" #include "pm_impl.h" #include "trax.h" +#include "esp_ota_ops.h" #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s @@ -175,6 +176,20 @@ void IRAM_ATTR call_start_cpu0() #endif ESP_EARLY_LOGI(TAG, "Pro cpu up."); +#if LOG_LOCAL_LEVEL >= ESP_LOG_INFO + const esp_app_desc_t *app_desc = esp_ota_get_app_description(); + ESP_EARLY_LOGI(TAG, "Application information:"); + ESP_EARLY_LOGI(TAG, "Project name: %s", app_desc->project_name); + ESP_EARLY_LOGI(TAG, "App version: %s", app_desc->version); +#ifdef CONFIG_APP_SECURE_VERSION + ESP_EARLY_LOGI(TAG, "Secure version: %x", app_desc->secure_version); +#endif +#ifdef CONFIG_APP_COMPILE_TIME_DATE + ESP_EARLY_LOGI(TAG, "Compile time: %s", app_desc->time); + ESP_EARLY_LOGI(TAG, "Compile date: %s", app_desc->date); +#endif + ESP_EARLY_LOGI(TAG, "ESP-IDF: %s", app_desc->idf_ver); +#endif #if !CONFIG_FREERTOS_UNICORE if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_DIS_APP_CPU)) { diff --git a/components/esp32/ld/esp32.common.ld.in b/components/esp32/ld/esp32.common.ld.in index a05d82983..7426dcb88 100644 --- a/components/esp32/ld/esp32.common.ld.in +++ b/components/esp32/ld/esp32.common.ld.in @@ -252,6 +252,9 @@ SECTIONS { _rodata_start = ABSOLUTE(.); + *(.rodata_desc .rodata_desc.*) /* Should be the first. App version info. DO NOT PUT ANYTHING BEFORE IT! */ + *(.rodata_custom_desc .rodata_custom_desc.*) /* Should be the second. Custom app version info. DO NOT PUT ANYTHING BEFORE IT! */ + mapping[flash_rodata] *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index 475d09dc1..e142b2a71 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -319,12 +319,18 @@ The following component-specific variables are available for use inside componen The following variables are set at the project level, but available for use in component CMakeLists: -- ``PROJECT_NAME``: Name of the project, as set in project Makefile +- ``PROJECT_NAME``: Name of the project, as set in project CMakeLists.txt file. - ``PROJECT_PATH``: Absolute path of the project directory containing the project Makefile. Same as the ``CMAKE_SOURCE_DIR`` variable. - ``COMPONENTS``: Names of all components that are included in this build, formatted as a semicolon-delimited CMake list. - ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. :doc:`More information here `. - ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``) - ``IDF_TARGET``: Name of the target for which the project is being built. +- ``PROJECT_VER``: Project version. + +* If ``PROJECT_VER`` variable set in project CMakeLists.txt file, its value will be used. +* Else, if the ``$PROJECT_PATH/version.txt`` exists, its contents will be used as ``PROJECT_VER``. +* Else, if the project is located inside a Git repository, the output of git describe will be used. +* Otherwise, ``PROJECT_VER`` will be empty. If you modify any of these variables inside ``CMakeLists.txt`` then this will not prevent other components from building but it may make your component hard to build and/or debug. @@ -403,6 +409,8 @@ The ESP-IDF build system adds the following C preprocessor definitions on the co - ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF. - ``IDF_VER`` — Defined to a git version string. E.g. ``v2.0`` for a tagged release or ``v1.0-275-g0efaa4f`` for an arbitrary commit. +- ``PROJECT_VER``: The project version, see `Preset Component Variables`_ for more details. +- ``PROJECT_NAME``: Name of the project, as set in project CMakeLists.txt file. Component Requirements ====================== diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index a0a3905ce..f52143cc8 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -187,6 +187,12 @@ The following variables are set at the project level, but exported for use in th - ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. - ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. - ``IDF_VER``: ESP-IDF version, retrieved from either ``$(IDF_PATH)/version.txt`` file (if present) else using git command ``git describe``. Recommended format here is single liner that specifies major IDF release version, e.g. ``v2.0`` for a tagged release or ``v2.0-275-g0efaa4f`` for an arbitrary commit. Application can make use of this by calling :cpp:func:`esp_get_idf_version`. +- ``PROJECT_VER``: Project version. + +* If ``PROJECT_VER`` variable set in project Makefile file, its value will be used. +* Else, if the ``$PROJECT_PATH/version.txt`` exists, its contents will be used as ``PROJECT_VER``. +* Else, if the project is located inside a Git repository, the output of git describe will be used. +* Otherwise, ``PROJECT_VER`` will be empty. If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug. @@ -302,6 +308,8 @@ ESP-IDF build systems adds the following C preprocessor definitions on the comma - ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF. - ``IDF_VER`` — ESP-IDF version, see `Preset Component Variables`_ for more details. +- ``PROJECT_VER``: The project version, see `Preset Component Variables`_ for more details. +- ``PROJECT_NAME``: Name of the project, as set in project Makefile. Build Process Internals ----------------------- diff --git a/docs/en/api-reference/system/system.rst b/docs/en/api-reference/system/system.rst index c8bdd97a7..1a2f9c480 100644 --- a/docs/en/api-reference/system/system.rst +++ b/docs/en/api-reference/system/system.rst @@ -109,6 +109,17 @@ SDK version :cpp:func:`esp_get_idf_version` returns a string describing the IDF version which was used to compile the application. This is the same value as the one available through ``IDF_VER`` variable of the build system. The version string generally has the format of ``git describe`` output. +App version +----------- +Application version is stored in :cpp:class:`esp_app_desc_t` structure. It is located in DROM sector and has a fixed offset from the beginning of the binary file. +The structure is located after :cpp:class:`esp_image_header_t` and :cpp:class:`esp_image_segment_header_t` structures. The field version has string type and max length 32 chars. + +To set version in your project manually you need set ``PROJECT_VER`` varible in your project Makefile/CMakeLists.txt: + +* For Make build system: in application Makefile put ``PROJECT_VER = "0.1.0.1"`` before including project.mk +* For Cmake build system: in application CMakeLists.txt put ``set(PROJECT_VER "0.1.0.1")`` before including project.cmake. + +If ``PROJECT_VER`` variable did not set in project Makefile/CMakeLists.txt then it can retrieved from either ``$(PROJECT_PATH)/version.txt`` file (if present) else using git command ``git describe``. Application can make use of this by calling :cpp:func:`esp_ota_get_app_description` or :cpp:func:`esp_ota_get_partition_description` functions. API Reference ------------- diff --git a/make/project.mk b/make/project.mk index feab67c5e..31e2e353c 100644 --- a/make/project.mk +++ b/make/project.mk @@ -278,6 +278,10 @@ LDFLAGS ?= -nostdlib \ CPPFLAGS ?= EXTRA_CPPFLAGS ?= CPPFLAGS := -DESP_PLATFORM -D IDF_VER=\"$(IDF_VER)\" -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) +PROJECT_VER ?= +export IDF_VER +export PROJECT_NAME +export PROJECT_VER # Warnings-related flags relevant both for C and C++ COMMON_WARNING_FLAGS = -Wall -Werror=all \ diff --git a/tools/ci/test_build_system.sh b/tools/ci/test_build_system.sh index 394aba510..c8c276815 100755 --- a/tools/ci/test_build_system.sh +++ b/tools/ci/test_build_system.sh @@ -224,6 +224,7 @@ function run_tests() clean_build_dir # Make provision for getting IDF version echo "custom-version-x.y" > ${IDF_PATH}/version.txt + echo "project-version-w.z" > ${TESTDIR}/template/version.txt # Hide .gitmodules so that submodule check is avoided [ -f ${IDF_PATH}/.gitmodules ] && mv ${IDF_PATH}/.gitmodules ${IDF_PATH}/.gitmodules_backup # Overload `git` command @@ -234,9 +235,24 @@ function run_tests() make [ -f ${IDF_PATH}/git_invoked ] && rm ${IDF_PATH}/git_invoked && failure "git should not have been invoked in this case" rm -f ${IDF_PATH}/version.txt git + rm -f ${TESTDIR}/template/version.txt [ -f ${IDF_PATH}/.gitmodules_backup ] && mv ${IDF_PATH}/.gitmodules_backup ${IDF_PATH}/.gitmodules export PATH=$OLD_PATH + print_status "Rebuild when app version was changed" + take_build_snapshot + # App version + echo "project-version-1.0" > ${TESTDIR}/template/version.txt + make + assert_rebuilt ${APP_BINS} + print_status "Change app version" + take_build_snapshot + echo "project-version-2.0" > ${TESTDIR}/template/version.txt + make + assert_rebuilt ${APP_BINS} + assert_not_rebuilt ${BOOTLOADER_BINS} esp32/libesp32.a + rm -f ${TESTDIR}/template/version.txt + print_status "Build fails if partitions don't fit in flash" cp sdkconfig sdkconfig.bak sed -i "s/CONFIG_ESPTOOLPY_FLASHSIZE.\+//" sdkconfig # remove all flashsize config @@ -278,7 +294,6 @@ mkdir -p ${TESTDIR} SNAPSHOT=${TESTDIR}/snapshot BUILD=${TESTDIR}/template/build - # copy all the build output to a snapshot directory function take_build_snapshot() { diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh index 7f138d561..92acd5965 100755 --- a/tools/ci/test_build_system_cmake.sh +++ b/tools/ci/test_build_system_cmake.sh @@ -96,6 +96,19 @@ function run_tests() idf.py build || failure "Partial build failed" assert_not_rebuilt ${ALL_BUILD_FILES} + print_status "Rebuild when app version was changed" + clean_build_dir + # App version + echo "project-version-1.0" > ${TESTDIR}/template/version.txt + idf.py build || failure "Failed to build with app version" + print_status "Change app version" + take_build_snapshot + echo "project-version-2.0" > ${TESTDIR}/template/version.txt + idf.py build || failure "Failed to rebuild with changed app version" + assert_rebuilt ${APP_BINS} + assert_not_rebuilt ${BOOTLOADER_BINS} esp-idf/esp32/libesp32.a + rm -f ${TESTDIR}/template/version.txt + print_status "Moving BUILD_DIR_BASE out of tree" clean_build_dir OUTOFTREE_BUILD=${TESTDIR}/alt_build diff --git a/tools/cmake/idf_functions.cmake b/tools/cmake/idf_functions.cmake index 5cf0f18dd..9453e17de 100644 --- a/tools/cmake/idf_functions.cmake +++ b/tools/cmake/idf_functions.cmake @@ -86,6 +86,7 @@ function(idf_set_global_compile_options) list(APPEND compile_options "${CMAKE_C_FLAGS}") list(APPEND c_compile_options "${CMAKE_C_FLAGS}") list(APPEND cxx_compile_options "${CMAKE_CXX_FLAGS}") + add_definitions(-DPROJECT_NAME=\"${PROJECT_NAME}\") if(CONFIG_OPTIMIZATION_LEVEL_RELEASE) list(APPEND compile_options "-Os") @@ -207,16 +208,42 @@ endfunction() # Running git_describe() here automatically triggers rebuilds # if the ESP-IDF git version changes function(idf_get_git_revision) + git_describe(IDF_VER_GIT "${IDF_PATH}") if(EXISTS "${IDF_PATH}/version.txt") file(STRINGS "${IDF_PATH}/version.txt" IDF_VER) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/version.txt") else() - git_describe(IDF_VER "${IDF_PATH}") + set(IDF_VER ${IDF_VER_GIT}) endif() + message(STATUS "IDF_VER: ${IDF_VER}") add_definitions(-DIDF_VER=\"${IDF_VER}\") git_submodule_check("${IDF_PATH}") set(IDF_VER ${IDF_VER} PARENT_SCOPE) endfunction() +# app_get_revision +# +# Set global PROJECT_VER +# +# If PROJECT_VER variable set in project CMakeLists.txt file, its value will be used. +# Else, if the _project_path/version.txt exists, its contents will be used as PROJECT_VER. +# Else, if the project is located inside a Git repository, the output of git describe will be used. +# Otherwise, PROJECT_VER will be empty. +function(app_get_revision _project_path) + git_describe(PROJECT_VER_GIT "${_project_path}") + if(NOT DEFINED PROJECT_VER) + if(EXISTS "${_project_path}/version.txt") + file(STRINGS "${_project_path}/version.txt" PROJECT_VER) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt") + else() + set(PROJECT_VER ${PROJECT_VER_GIT}) + endif() + endif() + message(STATUS "Project version: ${PROJECT_VER}") + git_submodule_check("${_project_path}") + set(PROJECT_VER ${PROJECT_VER} PARENT_SCOPE) +endfunction() + # idf_link_components # # Link library components to the target