]> granicus.if.org Git - esp-idf/commitdiff
esp32: Add firmware version to app
authorKonstantin Kondrashov <konstantin@espressif.com>
Fri, 5 Oct 2018 12:29:07 +0000 (20:29 +0800)
committerKonstantin Kondrashov <konstantin@espressif.com>
Mon, 3 Dec 2018 08:52:04 +0000 (16:52 +0800)
Added a new structure esp_app_desc_t. It has info about firmware:
version, secure_version, project_name, time/date build and IDF version.
Added the ability to add a custom structure with a description of the firmware.

The esp_app_desc_t is located in fixed place in start of ROM secotor. It is located after structures esp_image_header_t and esp_image_segment_header_t.

app_version is filed from PROJECT_VER variable (if set in custom make file) or PROJECT_PATH/version.txt or git repo (git describe).

Add API to get app_desc from partition.

22 files changed:
CMakeLists.txt
components/app_update/CMakeLists.txt
components/app_update/Kconfig.projbuild [new file with mode: 0644]
components/app_update/Makefile.projbuild
components/app_update/component.mk
components/app_update/esp_app_desc.c [new file with mode: 0644]
components/app_update/esp_ota_ops.c
components/app_update/include/esp_ota_ops.h
components/app_update/test/test_ota_ops.c
components/bootloader_support/include/bootloader_common.h
components/bootloader_support/include/esp_image_format.h
components/bootloader_support/src/bootloader_common.c
components/esp32/CMakeLists.txt
components/esp32/cpu_start.c
components/esp32/ld/esp32.common.ld.in
docs/en/api-guides/build-system-cmake.rst
docs/en/api-guides/build-system.rst
docs/en/api-reference/system/system.rst
make/project.mk
tools/ci/test_build_system.sh
tools/ci/test_build_system_cmake.sh
tools/cmake/idf_functions.cmake

index 226a67d5e02a043a39121f327374dab65c4fe013..fcf70d9aa9618cc373d978dfa321c834c9c71597 100644 (file)
@@ -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()
index e559ef10209741b885a89fb035aab00fb16068ca..2837244ad465d9605e443386a5bd918de2f519bd 100644 (file)
@@ -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 (file)
index 0000000..62f8bde
--- /dev/null
@@ -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
index c9ee59137309349d71b1c81a4b127f5c2f87b7e1..ebae19fee9a6bd87fe3119427911672cb7187e8a 100644 (file)
@@ -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)
index c2c4c03a1a6e2dc2a6bd58c0dd72d89d61b5d89d..924831e386c1094f6859057252c2c57782474175 100644 (file)
@@ -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 (file)
index 0000000..b120d2c
--- /dev/null
@@ -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 <assert.h>
+#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;
+}
index 6416b5d93f82ce464cc30d26ebcc2150cb0e9d54..fdab1b95cd02853b525b0b1e372c1f67c1106387 100644 (file)
@@ -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;
+}
index ca77b54226e002a19f41f0fe39a85f3540a980e0..3a2355d9e469ae0332ec755d2b66b02c530f7832 100644 (file)
@@ -20,6 +20,7 @@
 #include <stddef.h>
 #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
index e0a7864b5ffc3066381a5da496d1682647459faf..e7773ac24553892c138f2c433c449a9b633de83a 100644 (file)
@@ -8,7 +8,7 @@
 #include <unity.h>
 #include <test_utils.h>
 #include <esp_ota_ops.h>
-
+#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(&not_app_pos, &app_desc1));
+}
index d5a92cc79b6563068699b730b3c5810132a24bf4..d15d902ec09cd3c4207cf97bdd561ddad3852455 100644 (file)
@@ -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);
index bce3b1d7fad00159e0d8c503ddc4258e13dbd64d..7006cae98d43363a925d929559d658af07f0fefa 100644 (file)
@@ -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 */
index 479407845769ac5543707a5144c26caa5290180c..c4feb32b7da665e2088fc2dbd0b1b26e45f5ef19 100644 (file)
@@ -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;
+}
index d77aeb4102cf7d134de6850eee8ea70a8b0359c7..dfbd4b2c960fe79f2a84afde25058cd505702e6b 100644 (file)
@@ -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)
index 34dc885e6bc7e2debdd51e072357fc7c0d99e4d2..92ce879fe0f841620e6623df22ef5bb1313c9c51 100644 (file)
@@ -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)) {
index a05d829830fa30a3927f82babac33cd436553f0b..7426dcb8887715700d7fc8c77eb2beba2b691622 100644 (file)
@@ -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 */
index 475d09dc1faff2ffa37761d617de5c6550502f70..e142b2a71e121192b5be2c500673a677e2fdeda5 100644 (file)
@@ -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 </api-reference/kconfig>`.
 - ``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
 ======================
index a0a3905ceed4174e38c6caa6ee565c360aabef5d..f52143cc8c3104bc35ae90e45d1c61e0f13e5260 100644 (file)
@@ -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
 -----------------------
index c8bdd97a7b0e4ebfc581b1a124687b5bf787d54c..1a2f9c480a1dbe446f7f7380f2fe5a6f61d30c90 100644 (file)
@@ -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
 -------------
index feab67c5e2177ee244ef6773632e69e28f0442b3..31e2e353c46a02c51f33ea8eb02b1faf30e6030c 100644 (file)
@@ -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 \
index 394aba510c842422148b363d251626cb5606fba3..c8c2768156923d91b7a1a85ce0579e88a7eb6a36 100755 (executable)
@@ -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()
 {
index 7f138d56110ef958cd1ac296ad284b0f810b54ba..92acd596543e82dca9d70c971c5483383f04e99e 100755 (executable)
@@ -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
index 5cf0f18ddd312224f0906c5c30e852f942af9b60..9453e17dea48aec1cdc8adbf7ae002bd8130b320 100644 (file)
@@ -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