Build with Cogeno

Code generation has to be invoked as part of the build process of a project.

Build with CMake and Cogeno

Projects that use CMake to manage building the project can add the cogeno.cmake script (cogeno.cmake).

By this a file that contains inline code generation can be added to the project using the cogeno_sources command in the respective CMakeList.txt file.

cogeno_sources(target [EXTERN] [DELETE_SOURCE] [source_file..]
               [INCLUDES include_file...] [TXTFILES text_file...]
               [SOURCE_DIR dir] [INCLUDE_DIR dir] [TXTFILE_DIR dir]
               [COGENO_DEFINES defines..] [DEPENDS target.. file.. dir..])
  • EXTERN

    EXTERN has to be given in case the target was not created in the same CMakeList.txt file as the cogeno_sources command is issued.

  • DELETE_SOURCE

    If DELETE_SOURCE is given the generator code is removed from the output file.

  • SOURCE_DIR

    SOURCE_DIR specifies the directory to write the generated source file(s) to. A relative path is relative to ${CMAKE_CURRENT_BINARY_DIR}). It defaults to ${CMAKE_CURRENT_BINARY_DIR} or to the Cogeno SOURCE_DIR property if given. Do not set SOURCE_DIR in case the generated file is a compilable source file (eg. *.c, *.cpp), this will trigger CMake issue #14633.

  • INCLUDE_DIR

    INCLUDE_DIR specifies the directory to write the generated include file(s) to. A relative path is relative to ${CMAKE_CURRENT_BINARY_DIR}). It defaults to ${CMAKE_CURRENT_BINARY_DIR} or to the Cogeno INCLUDE_DIR property if given.

  • TXTFILE_DIR

    TXTFILE_DIR specifies the directory to write the generated text file(s) to. A relative path is relative to ${CMAKE_CURRENT_BINARY_DIR}). It defaults to ${CMAKE_CURRENT_BINARY_DIR} or to the Cogeno TXTFILE_DIR property if given.

  • INCLUDES

    INCLUDES marks files to be handled as include files (vs. source files).

  • TXTFILES

    TXTFILES marks files to be handled as text files (vs. source files). Text files are added as a dependency to the target.

  • COGENO_DEFINES

    The arguments given by the COGENO_DEFINES keyword have to be of the form define_name=define_value. The arguments become globals in the python snippets and can be accessed by define_name.

  • DEPENDS

    Dependencies given by the DEPENDS keyword are added to the dependencies of the generated file. Adding a dependency to a directory adds all files in that directory as a dependency.

Use Cogeno modules with CMake

The options provided to the modules are set by Cogeno CMake properties.

Module

Property

Usage

ccode

cmake

CMAKE_CACHE

Path to CMake cache file

CMAKE_DEFINES

CMake variable defines

config

:ref:`CONFIG_DB `

Path to config database file

CONFIG_FILE

Path to config file

CONFIG_KCONFIG_FILE

Path to Kconfig file

edts

EDTS_ARCH

Use the Cogeno EDTS module with CMake

EDTS_ARCH_FLAVOUR

EDTS_DTS

EDTS_DTS_ROOT

EDTS_DTS_PP_DEFINES

EDTS_DTS_PP_INCLUDE_DIRS

EDTS_DTS_PP_SOURCES

EDTS_BINDINGS_DIRS

EDTS_BINDINGS_EXCLUDE

EDTS_BINDINGS_NO_DEFAULT

EDTS_DB

protobuf

PROTOBUF_DB_DIR

Use the Cogeno protobuf module with CMake

PROTOBUF_SOURCES

PROTOBUF_INCLUDE_DIRS

zephyr

Use the Cogeno EDTS module with CMake

The cogeno.cmake script searches the list of directories in the EXTENSION_DIRS property and the edts or dts sub-directories within for device tree root pathes. If the directories exist they are added to the EDTS_DTS_ROOT property.

EXTENSION_DIRS sub-directory

Property/ usage

.

EDTS_DTS_ROOT

dts

EDTS_DTS_ROOT

edts

EDTS_DTS_ROOT

The EDTS_DTS_ROOT pathes are searched for a set of standard sub-directories. If available these sub-directories are added to the respective Cogeno EDTS properties.

EDTS_DTS_ROOT sub-directory

Property/ usage

include

EDTS_DTS_PP_INCLUDE_DIRS

include /dt-bindings

EDTS_DTS_PP_INCLUDE_DIRS

common

EDTS_DTS_PP_INCLUDE_DIRS

EDTS_ARCH

EDTS_DTS_PP_INCLUDE_DIRS

EDTS_ARCH/EDTS_ARCH_FLAVOUR

EDTS_DTS_PP_INCLUDE_DIRS

bindings

EDTS_BINDINGS_DIRS

Use the Cogeno protobuf module with CMake

The cogeno.cmake script searches the list of directories in the EXTENSION_DIRS property for a proto sub-directory. If the directory exists it is added to the PROTOBUF_INCLUDE_DIRS property.

The protobuf sources (*.proto) shall be added to the dependencies of the source file that is using the protobuf code database. The protobuf sources may either be given with absolute path or with a path relative to the CMakeList.txt directory.

The include directories that contain protobuf files to be imported by the protobuf sources, and that are not listed in the PROTOBUF_INCLUDE_DIRS property, shall also be added to the dependencies. The enclosing directories of the protobuf sources are automatically added.

CMakeList.txt

cogeno_sources('proto_user.c' DEPENDS proto/myproto.proto ../../general/proto)

Cogeno CMake properties

  • DEFINES

  • MODULES

  • TEMPLATES

  • EXTENSION_DIRS

  • CMAKE_CACHE

  • CMAKE_DEFINES

  • CONFIG_FILE

  • EDTS_ARCH

  • EDTS_ARCH_FLAVOUR

  • EDTS_DTS

  • EDTS_DTS_ROOT

  • EDTS_DTS_PP_DEFINES

  • EDTS_DTS_PP_INCLUDE_DIRS

  • EDTS_DTS_PP_SOURCES

  • EDTS_BINDINGS_DIRS

  • EDTS_BINDINGS_EXCLUDE

  • EDTS_BINDINGS_NO_DEFAULT

  • EDTS_DB

  • PROTOBUF_DB_DIR

  • PROTOBUF_SOURCES

  • PROTOBUF_INCLUDE_DIRS

Build with Zephyr and Cogeno

Zephyr uses the CMake build system with custom extensions. Build with CMake and Cogeno also applies to Zephyr builds.

To use Cogeno you have to include cogeno.cmake in your project’s CMakeList.txt file.

The easiest way to get cogeno.cmake and all of Cogeno with Zephyr is to install Cogeno as a Zephyr module.

west.yml

manifest:

  remotes:

    - name: gitlab_b0661
      url-base: https://gitlab.com/b0661

  projects:

    - name: cogeno
      remote: gitlab_b0661
      revision: master
      path: cogeno

Assure that on build the Cogeno module is processed before the modules that use it. If the sequence created by west –list does not suite your needs explicitly set ZEPHYR_MODULES in your project.

CMakeList.txt

set(ZEPHYR_MODULES
    ${CMAKE_CURRENT_SOURCE_DIR}/../cogeno
    ...
)

include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)

In Zephyr the processing of source files is controlled by the CMake extension functions zephyr.._sources_cogeno..(..) or zephyr_..includes_cogeno..(..). During build the source files are processed by Cogeno and the generated source files are written to the CMake binary directory. The generated source files are added to the Zephyr sources.

A file that contains inline code generation has to be added to the project by one of the following commands in a CMakeList.txt file:

zephyr_sources_cogeno(file [COGENO_DEFINES defines..] [DEPENDS target.. file..])
zephyr_sources_cogeno_ifdef(ifguard file [COGENO_DEFINES defines..] [DEPENDS target.. file..])
zephyr_library_sources_cogeno(file [COGENO_DEFINES defines..] [DEPENDS target.. file..])
zephyr_library_sources_cogeno_ifdef(ifguard file [COGENO_DEFINES defines..] [DEPENDS target.. file..])
zephyr_library_includes_cogeno(file.. [COGENO_DEFINES defines..] [DEPENDS target.. file..])
zephyr_library_includes_cogeno_ifdef(ifguard file.. [COGENO_DEFINES defines..] [DEPENDS target.. file..])

Use Cogeno modules with Zephyr

Use the Cogeno EDTS module with Zephyr

The project device tree file is processed twice by gen_defines.py of Zephyr and cogeno.edts() of Cogeno.

Usually the both should work on the same properties - but in some rare cases gen_defines.py of Zephyr may not understand certain properties because

  • the property is not foreseen for extraction

  • or the include directory for an include file for device tree preprocessing provided to the EDTS module is not provided to gen_defines.py (edts vs. dts)

  • or a binding file provided to the EDTS module is different from the one provided to gen_defines.py

In these cases you can use defines to exclude parts of the device tree file from processing by gen_defines.py. COGENO_EDTS is only defined if device tree preprocessing is done on behalf of Cogeno. If you need a more fine grained control you should add a define in the cogeno/<extension>.py Cogeno extension module of your project or Zephyr module (see Search for Cogeno extensions and Script Cogeno extension modules):

import cogeno
cogeno.option_argv_append('--edts:dts-pp-defines', "MY_DTS_DEFINE=1")

In the project device tree file you can control the DTS preprocessing in the usual way:

#if defined(MY_DTS_DEFINE)
#include <dt-bindings/my_special_driver/my_special_driver.h>
#endif

&my_special_driver {
#if defined(MY_DTS_DEFINE)
        clock-output-names = MY_SPECIAL_DRIVER_CLOCK_1 MY_SPECIAL_DRIVER_CLOCK_2
#endif
};

Build with ESP-IDF/ MDF and Cogeno

Cogeno can be integrated as a component into ESP32 projects using the ESP-IDF or ESP-MDF framework and the CMake build system. Build with CMake and Cogeno also applies to ESP-IDF/ ESP-MDF builds.

To create the Cogeno component make Cogeno a git submodule within the project’s components directory.

cd <project>/components
git submodule add https://gitlab.com/b0661/cogeno

Source files of components may use inline code generation. To generate the inline code the component sources have to be marked using the cogeno_sources() command in the respective CMakeList.txt file.

cogeno_sources() adds the generated source files to COGENO_COMPONENT_SRCS. It also adds include directories to COGENO_COMPONENT_INCLUDE_DIRS. The COGENO_COMPONENT_SRCS and the COGENO_COMPONENT_INCLUDE_DIRS have to be registered for the component.

Components should have the Cogeno component given in their PRIV_REQUIRES property.

if(NOT CMAKE_BUILD_EARLY_EXPANSION)
    cogeno_sources(${COMPONENT_NAME} inline_code_filea.c inline_code_fileb.c ...)
endif()
idf_component_register(
    SRCS ${COGENO_COMPONENT_SRCS} ...
    PRIV_REQUIRES cogeno ...
    INCLUDE_DIRS ${COGENO_COMPONENT_INCLUDE_DIRS} ...)

Use Cogeno modules with ESP-IDF and ESP-MDF

Use the Cogeno EDTS module with ESP-IDF and ESP-MDF

To use the Cogeno Extended Device Tree Specification (EDTS) module your project has to provide the top level device tree specification edts/project.dts. The project.dts file should include device tree source files from the main component and other components provided in their respective edts directory.

edts/project.dts

#include <boards/esp32/esp32.dts>

A special component holds most of the generic device tree source files for inclusion:

  • edts (see examples/esp_idf/edts)

cogeno.cmake

# Cogeno CMake example for build system integration.
# See examples/cmake/cogeno.cmake
#
# Cogeno properties - leave untouched for default:
# - DELETE_CODE
# - DEFINES
# - MODULES
# - TEMPLATES
# - EXTENSION_DIRS
# - CMAKE_CACHE
# - CMAKE_DEFINES
# - CONFIG_DB
# - CONFIG_FILE
# - CONFIG_INPUTS
# - CONFIG_KCONFIG_FILE
# - CONFIG_KCONFIG_SRCTREE
# - CONFIG_KCONFIG_DEFINES
# - EDTS_ARCH
# - EDTS_ARCH_FLAVOUR
# - EDTS_BINDINGS_DIRS
# - EDTS_BINDINGS_EXCLUDE
# - EDTS_BINDINGS_NO_DEFAULT
# - EDTS_DB
# - EDTS_DTS
# - EDTS_DTS_ROOT
# - EDTS_DTS_PP_DEFINES
# - EDTS_DTS_PP_INCLUDE_DIRS
# - EDTS_DTS_PP_SOURCES
# - PROTOBUF_DB_DIR
# - PROTOBUF_INCLUDE_DIRS
# - PROTOBUF_SOURCES

# protect against multiple inclusions
include_guard(GLOBAL)

# utilities
function(cogeno_unique_target_name_from_filename filename target_name)
    get_filename_component(basename ${filename} NAME)
    string(REPLACE "." "_" x ${basename})
    string(REPLACE "@" "_" x ${x})

    string(MD5 unique_chars ${filename})

    set(${target_name} cogeno_${x}_${unique_chars} PARENT_SCOPE)
endfunction()

##
# @brief Retrieve cogeno property of a project.
#
# @param var
# @param property
function(cogeno_get_property var property)
    get_property(is_set GLOBAL PROPERTY "COGENO_${property}" SET)
    if(${is_set})
        get_property(val GLOBAL PROPERTY "COGENO_${property}")
        set(${var} "${val}" PARENT_SCOPE)
    endif()
endfunction()

##
# @brief Set cogeno property on a project.
#
# @param property
# @param val
# @param APPEND
# @param UNIQUE
function(cogeno_set_property property val)
    set(options APPEND UNIQUE)
    cmake_parse_arguments(_ "${options}" "" "" ${ARGN})

    # Allow for relative path type properties
    cogeno_get_property(path_properties PATH_PROPERTIES)
    if(NOT path_properties)
        # Init standard cogeno path type properties
        list(APPEND path_properties
            MODULES TEMPLATES EXTENSION_DIRS CMAKE_CACHE
            CONFIG_DB CONFIG_FILE CONFIG_INPUTS
            CONFIG_KCONFIG_FILE CONFIG_KCONFIG_SRCTREE
            EDTS_DTS EDTS_DTS_PP_INCLUDE_DIRS EDTS_DTS_PP_SOURCES
            EDTS_BINDINGS_DIRS EDTS_DB PROTOBUF_DB_DIR PROTOBUF_INCLUDE_DIRS
            PROTOBUF_SOURCES)
        set_property(GLOBAL PROPERTY "COGENO_PATH_PROPERTIES" "${path_properties}")
    endif()
    if(${property} IN_LIST path_properties)
        if(NOT IS_ABSOLUTE ${val})
            set(val "${CMAKE_CURRENT_LIST_DIR}/${val}")
        endif()
        get_filename_component(val ${val} REALPATH)
    endif()

    if(__APPEND AND __UNIQUE)
        cogeno_get_property(property_val ${property})
        if(${val} IN_LIST property_val)
            return()
        endif()
    endif()

    if(__APPEND)
        set_property(GLOBAL APPEND PROPERTY "COGENO_${property}" "${val}")
    else()
        set_property(GLOBAL PROPERTY "COGENO_${property}" "${val}")
    endif()

    # Keep track of set cogeno properties
    cogeno_get_property(properties PROPERTIES)
    if(NOT property IN_LIST properties)
        cogeno_set_property(PROPERTIES ${property} APPEND)
    endif()
endfunction()

# Find Cogeno
#
# In case cogeno is installed at a custom location the cogeno INSTALL_PATH property
# may be set before calling FindCogeno().
# ``cogeno_set_property(INSTALL_PATH /install/path)``
#
# Result variables:
# ``Cogeno_FOUND``
# ``Cogeno_EXECUTABLE``
# ``Cogeno_EXECUTABLE_ARGS``
# ``Cogeno_BASE``
function(FindCogeno)
    cogeno_get_property(found FOUND)
    if(NOT DEFINED found)
        # Search for cogeno
        set(found FALSE)

        # We do the simple check - the example file was included - first.
        # If this is not the case we search for cogeno.py later on.
        #
        # If this file
        # - is named cogeno.cmake
        # - and is in examples/cmake/
        # - and there exists a cogeno.py in ../../cogeno
        # we know about the cogeno file.
        get_filename_component(cogeno_cmake_example ${CMAKE_CURRENT_LIST_FILE} ABSOLUTE)
        if(cogeno_cmake_example MATCHES ".*/examples/cmake/cogeno\.cmake")
            set(found TRUE)
            get_filename_component(base "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
            set(cogeno_py "${base}/cogeno/cogeno.py")
        endif()

        # Looking for Zephyr specific installation
        if(NOT ${found} AND DEFINED ZEPHYR_BASE AND EXISTS "${ZEPHYR_BASE}")
            # Preferred solution is cogeno as a west managed module
            if(ZEPHYR_MODULES)
                foreach(module ${ZEPHYR_MODULES})
                    set(full_path ${module}/cogeno/cogeno.py)
                    if(EXISTS ${full_path})
                        set(found TRUE)
                        set(base "${module}")
                        set(cogeno_py ${full_path})
                        break()
                    endif()
                endforeach()
            endif()
        endif()

        # Looking for ESP-IDF/MDF specific installation
        if(NOT ${found} AND DEFINED ESP_PLATFORM AND ESP_PLATFORM)
            # Preferred solution is cogeno as a component.
            # Git submodule of cogeno is in {component}/cogeno
            foreach(component_dir ${COMPONENT_DIRS})
                FILE(GLOB components LIST_DIRECTORIES true "${component_dir}")
                foreach(component ${components})
                    set(full_path ${component}/cogeno/cogeno/cogeno.py)
                    if(EXISTS ${full_path})
                        set(found TRUE)
                        set(base "${component}/cogeno" CACHE INTERNAL "cogeno base directory")
                        set(cogeno_py ${full_path})
                        break()
                    endif()
                endforeach()
                if(${found})
                    break()
                endif()
            endforeach()
        endif()

        # Looking for custom installation provided by cogeno property
        cogeno_get_property(install_path INSTALL_PATH)
        if(NOT ${found} AND DEFINED install_path)
            # take install path as top level cogeno directory
            set(full_path ${install_path}/cogeno/cogeno.py)
            if(EXISTS ${full_path})
                set(found TRUE)
                set(base "${install_path}")
                set(cogeno_py ${full_path})
            # search sub directories of install path
            else()
                FILE(GLOB modules LIST_DIRECTORIES true "${install_path}/*")
                foreach(module ${modules})
                    set(full_path ${module}/cogeno/cogeno.py)
                    if(EXISTS ${full_path})
                        set(found TRUE)
                        set(base "${module}")
                        set(cogeno_py ${full_path})
                        break()
                    endif()
                endforeach()
            endif()
        endif()

        # Do some further heuristics in case we have not found cogeno
        # Priorities:
        # - 1st priority: cogeno installed side by side to the project that hosts this file
        # - 2nd priority: cogeno installed on host
        # - 3rd priority: get cogeno from the git repository

        # cogeno installed side by side to the project that hosts this file
        # We do not know the nesting - just go up two or one level and search
        if(NOT ${found})
            # - 2 levels
            FILE(GLOB modules LIST_DIRECTORIES true "${CMAKE_SOURCE_DIR}/../../*")
            foreach(module ${modules})
                set(full_path ${module}/cogeno/cogeno.py)
                if(EXISTS ${full_path})
                    set(found TRUE)
                    set(base "${module}")
                    set(cogeno_py ${full_path})
                    break()
                endif()
            endforeach()
        endif()
        if(NOT ${found})
            # - 1 level
            FILE(GLOB modules LIST_DIRECTORIES true "${CMAKE_SOURCE_DIR}/../*")
            foreach(module ${modules})
                set(full_path ${module}/cogeno/cogeno.py)
                if(EXISTS ${full_path})
                    set(found TRUE)
                    set(base "${module}")
                    set(cogeno_py ${full_path})
                    break()
                endif()
            endforeach()
        endif()

        # cogeno installed on host
        if(NOT ${found})
            find_program(executable_found cogeno)

            if(EXISTS "${executable_found}")
                # Ask cogeno itself for base
                execute_process(${executable_found} "--base"
                                OUTPUT_VARIABLE base RESULT_VARIABLE ret)
                if(NOT ${ret} EQUAL 0)
                    message(FATAL_ERROR ${ret})
                endif()
                set(executable "${executable_found}")
                set(found TRUE)
            endif()
        endif()

        # Get cogeno from the git repository
        # Only install if a install path is given as a cogeno property.
        if(NOT ${found} AND DEFINED install_path)
            find_package(Git)
            if(NOT GIT_FOUND)
                message(FATAL_ERROR "git not found!")
            endif()
            if(NOT EXISTS "${install_path}")
                message(FATAL_ERROR "install path '${install_path}' does not exist!")
            endif()
            execute_process(
                COMMAND             ${GIT_EXECUTABLE} clone https://gitlab.com/b0661/cogeno.git --recursive
                WORKING_DIRECTORY   "${install_path}"
                OUTPUT_VARIABLE     git_output)
            message(STATUS "${git_output}")
            set(module "${install_path}/cogeno")
            if (EXISTS ${module})
                execute_process(
                    COMMAND             ${GIT_EXECUTABLE} checkout master
                    WORKING_DIRECTORY   "${module}"
                    OUTPUT_VARIABLE     git_output)
                message(STATUS "${git_output}")
                set(full_path ${module}/cogeno/cogeno.py)
                if(EXISTS ${full_path})
                    set(found TRUE)
                    set(base "${module}")
                    set(cogeno_py ${full_path})
                endif()
            endif()
        endif()

        cogeno_set_property(FOUND ${found})
        if(${found})
            # We may need a Python interpreter for cogeno
            if(DEFINED executable)
                # We do not need the Python 3 interpreter
                set(executable_args)
            else()
                if(${CMAKE_VERSION} VERSION_LESS "3.12")
                    set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5)
                    find_package(PythonInterp)

                    set(Python3_Interpreter_FOUND ${PYTHONINTERP_FOUND})
                    set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
                    set(Python3_VERSION ${PYTHON_VERSION_STRING})
                else()
                    # CMake >= 3.12
                    find_package(Python3 COMPONENTS Interpreter)
                endif()

                if(NOT ${Python3_Interpreter_FOUND})
                    message(FATAL_ERROR "Python 3 not found")
                endif()

                set(executable "${Python3_EXECUTABLE}")
                set(executable_args "${cogeno_py}")
            endif()
            cogeno_set_property(EXECUTABLE ${executable})
            cogeno_set_property(EXECUTABLE_ARGS ${executable_args})
            cogeno_set_property(BASE ${base})
        endif()
    endif()

    set(Cogeno_FOUND ${found} CACHE INTERNAL "cogeno found" PARENT_SCOPE)
    if(${found})
        cogeno_get_property(executable EXECUTABLE)
        set(Cogeno_EXECUTABLE ${executable}
            CACHE INTERNAL "cogeno executable"  PARENT_SCOPE)
        cogeno_get_property(executable_args EXECUTABLE_ARGS)
        set(Cogeno_EXECUTABLE_ARGS ${executable_args}
            CACHE INTERNAL "cogeno executable arguments" PARENT_SCOPE)
        cogeno_get_property(base BASE)
        set(Cogeno_BASE ${base}
            CACHE INTERNAL "cogeno base directory" PARENT_SCOPE)
    endif()
endfunction()

function(cogeno_init_properties_zephyr)
    # Set directories to be searched for cogeno extensions
    foreach(extension_dir ${DTS_ROOT} ${ZEPHYR_MODULES})
        get_filename_component(extension_dir "${extension_dir}" REALPATH)
        cogeno_set_property(EXTENSION_DIRS ${extension_dir} APPEND UNIQUE)
    endforeach()

    if(EXISTS "${APPLICATION_SOURCE_DIR}/templates")
        cogeno_set_property(TEMPLATES "${APPLICATION_SOURCE_DIR}/templates" APPEND UNIQUE)
        cogeno_set_property(MODULES "${APPLICATION_SOURCE_DIR}/templates" APPEND UNIQUE)
    endif()
    if(EXISTS "${PROJECT_SOURCE_DIR}/templates")
        cogeno_set_property(TEMPLATES "${PROJECT_SOURCE_DIR}/templates" APPEND UNIQUE)
        cogeno_set_property(MODULES "${PROJECT_SOURCE_DIR}/templates" APPEND UNIQUE)
    endif()

    # DOTCONFIG and merge_config_files is created by Zephyr - use it to get the config fragements
    set(config_file "${DOTCONFIG}")
    cogeno_set_property(CONFIG_FILE "${config_file}")
    foreach(config_fragment ${merge_config_files})
        if(${config_fragment} STREQUAL ${config_file})
            continue()
        endif()
        cogeno_set_property(CONFIG_INPUTS "${config_fragment}" APPEND UNIQUE)
    endforeach()
    cogeno_set_property(CONFIG_KCONFIG_FILE "${KCONFIG_ROOT}")
    cogeno_set_property(CONFIG_KCONFIG_SRCTREE "${ZEPHYR_BASE}")
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "ZEPHYR_BASE=${ZEPHYR_BASE}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "KERNELVERSION=${KERNELVERSION}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "KCONFIG_CONFIG=${config_file}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "ARCH=${ARCH}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "BOARD_DIR=${BOARD_DIR}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "SHIELD_AS_LIST=${SHIELD_AS_LIST}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "KCONFIG_BINARY_DIR=${KCONFIG_BINARY_DIR}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "ARCH_DIR=${ARCH_DIR}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "TOOLCHAIN_KCONFIG_DIR=${TOOLCHAIN_KCONFIG_DIR}" APPEND UNIQUE)
    cogeno_set_property(CONFIG_KCONFIG_DEFINES "EDT_PICKLE=${EDT_PICKLE}" APPEND UNIQUE)

    cogeno_set_property(CMAKE_DEFINES "APPLICATION_SOURCE_DIR" APPEND UNIQUE)
    cogeno_set_property(CMAKE_DEFINES "APPLICATION_BINARY_DIR" APPEND UNIQUE)

    cogeno_set_property(EDTS_ARCH ${ARCH})

    # Set EDTS_DTS_PP_SOURCES property
    if(NOT DEFINED DTS_SOURCE)
        message(STATUS "Cogeno can't create EDTS - Zephyr 'DTS_SOURCE' not provided")
    elseif(NOT EXISTS ${DTS_SOURCE})
        message(STATUS "Cogeno can't create EDTS - '${DTS_SOURCE}' not found")
    else()
        cogeno_set_property(EDTS_DTS_PP_SOURCES "${DTS_SOURCE}")
        if(DTC_OVERLAY_FILE)
            # DTC_OVERLAY_FILE is a space-separated list
            foreach(overlay_file ${DTC_OVERLAY_FILE})
                if(EXISTS ${overlay_file})
                    cogeno_set_property(EDTS_DTS_PP_SOURCES "${overlay_file}" APPEND UNIQUE)
                else()
                    message(STATUS "Cogeno can't create EDTS - '${overlay_file}' not found")
                endif()
            endforeach()
        endif()
    endif()

    # Set EDTS_DTS_PP_DEFINES
    cogeno_set_property(EDTS_DTS_PP_DEFINES "__DTC__" APPEND UNIQUE)

    # Set EDTS_DTS_ROOT property
    if(NOT DEFINED DTS_ROOT)
        message(STATUS "Cogeno may not create EDTS - Zephyr 'DTS_ROOT' not provided")
    endif()

    # Set EDTS_BINDINGS_DIRS property
    if(NOT DEFINED DTS_ROOT_BINDINGS)
        message(STATUS "Cogeno may not create EDTS - Zephyr 'DTS_ROOT_BINDINGS' not provided")
    else()
        string(REPLACE "?" ";" root_bindings "${DTS_ROOT_BINDINGS}")
        list(APPEND edts_bindings_dirs ${root_bindings})
        list(REMOVE_DUPLICATES edts_bindings_dirs)
        foreach(binding_dir ${edts_bindings_dirs})
            if(EXISTS ${binding_dir})
                cogeno_set_property(EDTS_BINDINGS_DIRS ${binding_dir} APPEND UNIQUE)
            endif()
        endforeach()
    endif()

    # Set EDTS_BINDINGS_EXCLUDE property
    # Exclude generic bindings that are provided by cogeno
    foreach(binding_exclude
        "${ZEPHYR_BASE}/dts/bindings/iio/adc/adc-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/base/base.yaml"
        "${ZEPHYR_BASE}/dts/bindings/can/can-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/can/can-device.yaml"
        "${ZEPHYR_BASE}/dts/bindings/clock/clock-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/clock/fixed-clock.yaml"
        "${ZEPHYR_BASE}/dts/bindings/cpu/cpu.yaml"
        "${ZEPHYR_BASE}/dts/bindings/dma/dma-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/espi/espi-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/ethernet/ethernet.yaml"
        "${ZEPHYR_BASE}/dts/bindings/flash_controller/flash-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/gpio/gpio-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/gpio/gpio-keys.yaml"
        "${ZEPHYR_BASE}/dts/bindings/gpio/gpio-leds.yaml"
        "${ZEPHYR_BASE}/dts/bindings/gpio/gpio-nexus.yaml"
        "${ZEPHYR_BASE}/dts/bindings/i2c/i2c-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/i2c/i2c-device.yaml"
        "${ZEPHYR_BASE}/dts/bindings/i2s/i2s-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/i2s/i2s-device.yaml"
        "${ZEPHYR_BASE}/dts/bindings/interrupt-controller/interrupt-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/interrupt-controller/shared-irq.yaml"
        "${ZEPHYR_BASE}/dts/bindings/kscan/kscan.yaml"
        "${ZEPHYR_BASE}/dts/bindings/led/pwm-leds.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mmc/mmc-spi-slot.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mmc/mmc.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mtd/eeprom-base.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mtd/eeprom-spi-i2c.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mtd/partition.yaml"
        "${ZEPHYR_BASE}/dts/bindings/mtd/soc-nv-flash.yaml"
        "${ZEPHYR_BASE}/dts/bindings/phy/phy-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/ps2/ps2.yaml"
        "${ZEPHYR_BASE}/dts/bindings/pwm/pwm-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/rtc/rtc.yaml"
        "${ZEPHYR_BASE}/dts/bindings/serial/uart-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/serial/uart-device.yaml"
        "${ZEPHYR_BASE}/dts/bindings/spi/spi-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/spi/spi-device.yaml"
        "${ZEPHYR_BASE}/dts/bindings/sram/mmio-sram.yaml"
        "${ZEPHYR_BASE}/dts/bindings/usb/usb-controller.yaml"
        "${ZEPHYR_BASE}/dts/bindings/usb/usb-ep.yaml")
        cogeno_set_property(EDTS_BINDINGS_EXCLUDE ${binding_exclude} APPEND UNIQUE)
    endforeach()

endfunction()

function(cogeno_postprocess_properties_zephyr)
    # Make the bindings include dirs publically available
    cogeno_get_property(edts_dts_pp_include_dirs EDTS_DTS_PP_INCLUDE_DIRS)
    zephyr_include_directories(${edts_dts_pp_include_dirs})
endfunction()

function(cogeno_init_properties_esp_platform)

    # Search for components
    # - extension dirs
    # - cogeno component
    # - main component
    # - edts component ???
    #
    # Take always the last component directory found.
    #
    # Explanation from IDF documentation:
    # This allows, for example, overriding ESP-IDF components with
    # a modified version by copying that component from the ESP-IDF
    # components directory to the project components directory and
    # then modifying it there. If used in this way, the ESP-IDF
    # directory itself can remain untouched.
    idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS)
    foreach(component_target ${build_component_targets})
        __component_get_property(component_dir ${component_target} COMPONENT_DIR)
        __component_get_property(component_name ${component_target} COMPONENT_NAME)
        # Set directories to be searched for cogeno extensions
        cogeno_set_property(EXTENSION_DIRS ${component_dir} APPEND UNIQUE)
        # cogeno component
        set(full_path ${component_dir}/cogeno/cogeno/cogeno.py)
        if(EXISTS ${full_path})
            cogeno_set_property(COMPONENT_COGENO "${component}")
        endif()
    endforeach()
    # main component
    if(EXISTS "${PROJECT_SOURCE_DIR}/main")
        cogeno_set_property(COMPONENT_MAIN "${PROJECT_SOURCE_DIR}/main")
    endif()

    cogeno_set_property(CONFIG_FILE "${SDKCONFIG}")

    cogeno_set_property(CMAKE_DEFINES "SDKCONFIG" APPEND)
    cogeno_set_property(CMAKE_DEFINES "SDKCONFIG_DEFAULTS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "BUILD_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "COMPONENTS" APPEND)

    cogeno_set_property(CMAKE_DEFINES "COMPONENT_ALIAS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "COMPONENT_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "COMPONENT_LIB" APPEND)
    cogeno_set_property(CMAKE_DEFINES "COMPONENT_NAME" APPEND)
    cogeno_set_property(CMAKE_DEFINES "COMPONENT_TYPE" APPEND)
    cogeno_set_property(CMAKE_DEFINES "EMBED_FILES" APPEND)
    cogeno_set_property(CMAKE_DEFINES "EMBED_TXTFILES" APPEND)
    cogeno_set_property(CMAKE_DEFINES "INCLUDE_DIRS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "KCONFIG" APPEND)
    cogeno_set_property(CMAKE_DEFINES "KCONFIG_PROJBUILD" APPEND)
    cogeno_set_property(CMAKE_DEFINES "LDFRAGMENTS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "PRIV_INCLUDE_DIRS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "PRIV_REQUIRES" APPEND)
    cogeno_set_property(CMAKE_DEFINES "REQUIRED_IDF_TARGETS" APPEND)
    cogeno_set_property(CMAKE_DEFINES "REQUIRES" APPEND)
    cogeno_set_property(CMAKE_DEFINES "SRCS" APPEND)

    # Set EDTS system architecture
    cogeno_set_property(EDTS_ARCH "xtensa")
    cogeno_set_property(EDTS_ARCH_FLAVOUR "espressif")

    # Set EDTS_DTS_PP_SOURCES property
    set(full_path "${PROJECT_SOURCE_DIR}/edts/project.dts")
    if(NOT EXISTS ${full_path})
        message(STATUS "Cogeno can't create EDTS - '${full_path}' not found")
    else()
        cogeno_set_property(EDTS_DTS_PP_SOURCES "${full_path}")
    endif()

endfunction()

function(cogeno_postprocess_properties_esp_platform)
endfunction()

function(cogeno_init_properties_unknown_platform)
endfunction()

function(cogeno_postprocess_properties_unknown_platform)
endfunction()

function(cogeno_init_properties)
    # Generic properties provided by FindCogeno are:
    # - FOUND
    # - EXECUTABLE
    # - EXECUTABLE_ARGS
    # - BASE
    # Properties that are set to default values before platform init:
    # - DELETE_CODE
    # - EXTENSION_DIRS
    # - CONFIG_DB
    # - CMAKE_DEFINES
    # - EDTS_DB
    # - EDTS_DTS
    # - PLATFORM
    # - PROTOBUF_DB_DIR
    # Generic properties provided by platform init:
    # - CONFIG_FILE
    # - CONFIG_INPUTS
    # - CONFIG_KCONFIG_FILE
    # - CONFIG_KCONFIG_SRCTREE
    # - CONFIG_KCONFIG_DEFINES
    # - CMAKE_CACHE
    # - EDTS_ARCH
    # - EDTS_ARCH_FLAVOUR
    # - EDTS_DTS_PP_SOURCES
    # - EDTS_DTS_PP_DEFINES
    # Properties that are set to default values after platform init:
    # - MODULES
    # - TEMPLATES
    # - EDTS_BINDINGS_DIRS
    # - EDTS_DTS_PP_INCLUDE_DIRS
    # - EDTS_DTS_ROOT
    # - PROTOBUF_INCLUDE_DIRS

    # Assure cogeno is available
    # --------------------------
    cogeno_get_property(Cogeno_FOUND FOUND)
    if(NOT DEFINED Cogeno_FOUND)
        FindCogeno()
    endif()
    if(NOT Cogeno_FOUND)
        message(FATAL "Cogeno not found")
    endif()

    # directories to be searched for extended device tree sources, bindings, includes
    set(edts_dirs "edts" "dts")

    # Set pre platform init default properties
    # ----------------------------------------
    cogeno_set_property(DELETE_CODE FALSE)

    cogeno_set_property(EXTENSION_DIRS "${PROJECT_SOURCE_DIR}" APPEND)

    cogeno_set_property(LOG "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cogeno.log")
    cogeno_set_property(LOCK "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cogeno.lock")

    cogeno_set_property(CMAKE_CACHE "${CMAKE_BINARY_DIR}/CMakeCache.txt")

    cogeno_set_property(CMAKE_DEFINES "PROJECT_NAME" APPEND)
    cogeno_set_property(CMAKE_DEFINES "PROJECT_SOURCE_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "PROJECT_BINARY_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_SOURCE_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_BINARY_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_CURRENT_SOURCE_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_CURRENT_BINARY_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_CURRENT_LIST_DIR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_FILES_DIRECTORY" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_PROJECT_NAME" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_SYSTEM" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_SYSTEM_NAME" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_SYSTEM_VERSION" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_SYSTEM_PROCESSOR" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_C_COMPILER" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_CXX_COMPILER" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_COMPILER_IS_GNUCC" APPEND)
    cogeno_set_property(CMAKE_DEFINES "CMAKE_COMPILER_IS_GNUCXX" APPEND)

    cogeno_set_property(CONFIG_DB "${CMAKE_BINARY_DIR}/config.json")

    cogeno_set_property(EDTS_DB "${CMAKE_BINARY_DIR}/edts.json")
    cogeno_set_property(EDTS_DTS "${CMAKE_BINARY_DIR}/edts.dts")
    cogeno_set_property(EDTS_DTS_PP_DEFINES "COGENO_EDTS=1")

    cogeno_set_property(PROTOBUF_DB_DIR "${CMAKE_BINARY_DIR}/protobuf")

    # Set platform init properties
    # ----------------------------
    if(DEFINED ZEPHYR_BASE AND EXISTS "${ZEPHYR_BASE}")
        cogeno_set_property(PLATFORM "zephyr")
        cogeno_init_properties_zephyr()
    elseif(DEFINED ESP_PLATFORM AND ESP_PLATFORM)
        cogeno_set_property(PLATFORM "esp")
        cogeno_init_properties_esp_platform()
    else()
        cogeno_set_property(PLATFORM "generic")
        cogeno_init_properties_unknown_platform()
    endif()

    # Set post platform init default properties
    # -----------------------------------------

    # Update EXTENSION_DIRS for cogeno extensions
    cogeno_get_property(extension_dirs EXTENSION_DIRS)
    # Report EXTENSION_DIRS
    foreach(extension_dir ${extension_dirs})
        message(VERBOSE "Cogeno found extension dir '${extension_dir}'")
    endforeach()

    # Set MODULES and TEMPLATES
    foreach(extension_dir ${extension_dirs})
        set(full_path "${extension_dir}/cogeno/modules")
        if(EXISTS ${full_path})
            cogeno_set_property(MODULES "${full_path}" APPEND UNIQUE)
        endif()
        set(full_path "${extension_dir}/cogeno/templates")
        if(EXISTS ${full_path})
            cogeno_set_property(TEMPLATES "${full_path}" APPEND UNIQUE)
        endif()
    endforeach()
    cogeno_get_property(modules MODULES)
    cogeno_get_property(templates TEMPLATES)
    # Report MODULES
    foreach(module ${modules})
        message(VERBOSE "Cogeno found module dir '${module}'")
    endforeach()
    # Report TEMPLATES
    foreach(template ${templates})
        message(VERBOSE "Cogeno found template dir '${template}'")
    endforeach()

    # Set EDTS_DTS_ROOT property
    foreach(extension_dir ${extension_dirs})
        # Use <dir>/include/dt-bindings as DTS root dir
        set(full_path "${extension_dir}/include/dt-bindings")
        if(EXISTS ${full_path})
            cogeno_set_property(EDTS_DTS_ROOT "${extension_dir}" APPEND UNIQUE)
        endif()
        # Search for <dir>/edts and/or <dir>/dts
        foreach(edts_dir ${edts_dirs})
            set(full_path "${extension_dir}/${edts_dir}")
            if(EXISTS ${full_path})
                cogeno_set_property(EDTS_DTS_ROOT "${full_path}" APPEND UNIQUE)
            endif()
        endforeach()
    endforeach()
    cogeno_get_property(edts_dts_root EDTS_DTS_ROOT)
    # Report EDTS_DTS_ROOT
    foreach(dts_root ${edts_dts_root})
        message(VERBOSE "Cogeno found EDT specification root '${dts_root}'")
    endforeach()

    # Set EDTS_BINDINGS_DIRS property
    foreach(dts_root ${edts_dts_root})
        # Bindings given in dts root
        set(full_path "${dts_root}/bindings")
        if(EXISTS ${full_path})
            cogeno_set_property(EDTS_BINDINGS_DIRS "${full_path}" APPEND UNIQUE)
        endif()
        # This is the device tree component - use skeleton.dtsi to detect
        set(full_path "${dts_root}/common/skeleton.dtsi")
        if(EXISTS ${full_path})
            cogeno_set_property(COMPONENT_EDTS "${dts_root}" APPEND UNIQUE)
        endif()
    endforeach()
    cogeno_get_property(edts_bindings_dirs EDTS_BINDINGS_DIRS)
    cogeno_get_property(component_edts COMPONENT_EDTS)
    # Report EDTS_BINDINGS_DIRS
    foreach(edts_bindings_dir ${edts_bindings_dirs})
        message(VERBOSE "Cogeno found EDT bindings dir '${edts_bindings_dir}'")
    endforeach()
    # Report COMPONENT_EDTS
    foreach(component ${component_edts})
        message(VERBOSE "Cogeno found EDT specification component '${component}'")
    endforeach()

    # Get system architecture and create sub dts root architecture standard pathes
    cogeno_get_property(edts_arch EDTS_ARCH)
    if(DEFINED edts_arch)
        set(edts_arch_path "${edts_arch}")
        cogeno_get_property(edts_arch_flavour EDTS_ARCH_FLAVOUR)
        if(DEFINED edts_arch_flavour)
            set(edts_arch_flavour_path "${edts_arch_path}/${edts_arch_flavour}")
        endif()
    endif()

    # Set EDTS_DTS_PP_INCLUDE_DIRS property
    foreach(dts_root ${edts_dts_root})
        cogeno_set_property(EDTS_DTS_PP_INCLUDE_DIRS "${dts_root}" APPEND UNIQUE)
        set(full_path "${dts_root}/include/dt-bindings")
        if(EXISTS ${full_path})
            cogeno_set_property(EDTS_DTS_PP_INCLUDE_DIRS "${dts_root}/include" APPEND UNIQUE)
        endif()
        foreach(dts_root_standard_path include common ${edts_arch_path} ${edts_arch_flavour_path})
            set(full_path ${dts_root}/${dts_root_standard_path})
            if(EXISTS ${full_path})
                cogeno_set_property(EDTS_DTS_PP_INCLUDE_DIRS "${full_path}" APPEND UNIQUE)
            endif()
        endforeach()
    endforeach()
    cogeno_get_property(edts_dts_pp_include_dirs EDTS_DTS_PP_INCLUDE_DIRS)
    # Report EDTS_DTS_PP_INCLUDE_DIRS
    foreach(edts_bindings_include_dir ${edts_dts_pp_include_dirs})
        message(VERBOSE "Cogeno found EDT bindings include dir '${edts_bindings_include_dir}'")
    endforeach()

    # Set EDTS_DTS_PP_SOURCES property - already set by platform code
    cogeno_get_property(edts_dts_pp_sources EDTS_DTS_PP_SOURCES)
    # Report EDTS_DTS_PP_SOURCES
    foreach(edts_dts_pp_source ${edts_dts_pp_sources})
        message(VERBOSE "Cogeno found DTS source '${edts_dts_pp_source}'")
    endforeach()

    # Set EDTS_DTS_PP_DEFINES
    # - add platform defines
    cogeno_get_property(platform PLATFORM)
    if(DEFINED platform)
        string(TOUPPER "${platform}" platform_uc)
        cogeno_set_property(EDTS_DTS_PP_DEFINES "COGENO_${platform_uc}=1" APPEND UNIQUE)
    endif()
    # - add architecture defines
    cogeno_get_property(edts_arch EDTS_ARCH)
    if(DEFINED edts_arch)
        string(TOUPPER "${edts_arch}" edts_arch_uc)
        cogeno_set_property(EDTS_DTS_PP_DEFINES "COGENO_EDTS_ARCH_${edts_arch_uc}=1" APPEND UNIQUE)
        cogeno_get_property(edts_arch_flavour EDTS_ARCH_FLAVOUR)
        if(DEFINED edts_arch_flavour)
            string(TOUPPER "${edts_arch_flavour}" edts_arch_flavour_uc)
            cogeno_set_property(EDTS_DTS_PP_DEFINES "COGENO_EDTS_ARCH_${edts_arch_uc}_${edts_arch_flavour_uc}=1" APPEND UNIQUE)
        endif()
    endif()
    cogeno_get_property(edts_dts_pp_defines EDTS_DTS_PP_DEFINES)
    # Report EDTS_DTS_PP_DEFINES
    foreach(edts_dts_pp_define ${edts_dts_pp_defines})
        message(VERBOSE "Cogeno found DTS preprocessor define '${edts_dts_pp_define}'")
    endforeach()

    # Set PROTOBUF_INCLUDE_DIRS property
    foreach(extension_dir ${extension_dirs})
        # Use <dir>/proto as include dir
        set(full_path "${extension_dir}/proto")
        if(EXISTS ${full_path})
            cogeno_set_property(PROTOBUF_INCLUDE_DIRS "${extension_dir}" APPEND UNIQUE)
        endif()
    endforeach()
    cogeno_get_property(protobuf_include_dirs PROTOBUF_INCLUDE_DIRS)
    # Report PROTOBUF_INCLUDE_DIRS
    foreach(protobuf_include_dir ${protobuf_include_dirs})
        message(VERBOSE "Cogeno found protobuf include directory '${protobuf_include_dir}'")
    endforeach()

    # Set platform post init properties
    # ---------------------------------
    if(DEFINED ZEPHYR_BASE AND EXISTS "${ZEPHYR_BASE}")
        cogeno_postprocess_properties_zephyr()
    elseif(DEFINED ESP_PLATFORM AND ESP_PLATFORM)
        cogeno_postprocess_properties_esp_platform()
    else()
        cogeno_postprocess_properties_unknown_platform()
    endif()

    cogeno_set_property(PROPERTIES_INITIALIZED TRUE)
endfunction()


function(cogeno_set_options
    args_source_dir
    args_include_dir
    args_txtfile_dir
    args_delete_code
    args_cogeno_defines
    args_depends)

    cogeno_get_property(properties_initialized PROPERTIES_INITIALIZED)
    if(NOT DEFINED properties_initialized)
        # Global cogeno properties not initialized - init now
        cogeno_init_properties()
    endif()

    # cogeno executable
    cogeno_get_property(executable EXECUTABLE)
    set(cogeno_opt_executable ${executable}  PARENT_SCOPE)
    cogeno_get_property(executable_args EXECUTABLE_ARGS)
    set(cogeno_opt_executable_args ${executable_args} PARENT_SCOPE)
    cogeno_get_property(base BASE)
    set(cogeno_opt_base ${base} PARENT_SCOPE)

    # directory to put the sources in
    cogeno_get_property(source_dir SOURCE_DIR)
    if(NOT "${args_source_dir}" STREQUAL "")
        set(cogeno_opt_source_dir "${args_source_dir}")
    elseif(DEFINED source_dir AND source_dir)
        set(cogeno_opt_source_dir "${source_dir}")
    else()
        set(cogeno_opt_source_dir "${CMAKE_CURRENT_BINARY_DIR}")
    endif()
    if(NOT IS_ABSOLUTE ${cogeno_opt_source_dir})
        # relative path - use whole relative path
        set(cogeno_opt_source_dir ${CMAKE_CURRENT_BINARY_DIR}/${cogeno_opt_source_dir})
    endif()
    set(cogeno_opt_source_dir ${cogeno_opt_source_dir} PARENT_SCOPE)

    # directory to put the includes in
    cogeno_get_property(include_dir INCLUDE_DIR)
    if(NOT "${args_include_dir}" STREQUAL "")
        set(cogeno_opt_include_dir "${args_include_dir}")
    elseif(DEFINED include_dir AND include_dir)
        set(cogeno_opt_include_dir "${include_dir}")
    else()
        set(cogeno_opt_include_dir "${CMAKE_CURRENT_BINARY_DIR}")
    endif()
    if(NOT IS_ABSOLUTE ${cogeno_opt_include_dir})
        # relative path - use whole relative path
        set(cogeno_opt_include_dir ${CMAKE_CURRENT_BINARY_DIR}/${cogeno_opt_include_dir})
    endif()
    set(cogeno_opt_include_dir ${cogeno_opt_include_dir} PARENT_SCOPE)

    # directory to put the text files in
    cogeno_get_property(txtfile_dir TXTFILE_DIR)
    if(NOT "${args_txtfile_dir}" STREQUAL "")
        set(cogeno_opt_txtfile_dir "${args_txtfile_dir}")
    elseif(DEFINED txtfile_dir AND txtfile_dir)
        set(cogeno_opt_txtfile_dir "${txtfile_dir}")
    else()
        set(cogeno_opt_txtfile_dir "${CMAKE_CURRENT_BINARY_DIR}")
    endif()
    if(NOT IS_ABSOLUTE ${cogeno_opt_txtfile_dir})
        # relative path - use whole relative path
        set(cogeno_opt_txtfile_dir ${CMAKE_CURRENT_BINARY_DIR}/${cogeno_opt_txtfile_dir})
    endif()
    set(cogeno_opt_txtfile_dir ${cogeno_opt_txtfile_dir} PARENT_SCOPE)

    # generated files dependencies
    set(cogeno_opt_depends)
    foreach(depend ${args_depends})
        if(TARGET ${depend})
            list(APPEND cogeno_opt_depends ${depend})
        endif()
        # Find path of dependency
        if(NOT IS_ABSOLUTE ${depend})
            # relative path - use whole relative path
            set(depend ${CMAKE_CURRENT_SOURCE_DIR}/${depend})
        endif()
        if(IS_DIRECTORY "${depend}")
            FILE(GLOB depends LIST_DIRECTORIES false "${depend}/*")
            if(depends)
                list(APPEND cogeno_opt_depends ${depends})
            endif()
        else()
            list(APPEND cogeno_opt_depends ${depend})
        endif()
    endforeach()
    set(cogeno_opt_depends ${cogeno_opt_depends} PARENT_SCOPE)

    # --lock
    cogeno_get_property(lock LOCK)
    if(DEFINED lock AND lock)
        set(cogeno_opt_lock "--lock" ${lock})
    else()
        set(cogeno_opt_lock)
    endif()
    set(cogeno_opt_lock ${cogeno_opt_lock} PARENT_SCOPE)

    # --lock
    cogeno_get_property(log LOG)
    if(DEFINED log AND log)
        set(cogeno_opt_log "--log" ${log})
    else()
        set(cogeno_opt_log)
    endif()
    set(cogeno_opt_log ${cogeno_opt_log} PARENT_SCOPE)

    # -x
    cogeno_get_property(delete_code DELETE_CODE)
    if((DEFINED delete_code AND delete_code) OR args_delete_code)
        set(cogeno_opt_delete_code '-x')
    else()
        set(cogeno_opt_delete_code)
    endif()
    set(cogeno_opt_delete_code ${cogeno_opt_delete_code} PARENT_SCOPE)

    # -D
    cogeno_get_property(defines DEFINES)
    if((DEFINED defines AND defines) OR args_cogeno_defines)
        string(REGEX REPLACE "([^;]+)" "-D;\\1"
              cogeno_opt_defines "${defines};${args_cogeno_defines}")
    else()
        set(cogeno_opt_defines)
    endif()
    set(cogeno_opt_defines ${cogeno_opt_defines} PARENT_SCOPE)

    # --extensions
    cogeno_get_property(extensions EXTENSION_DIRS)
    if(DEFINED extensions AND extensions)
        set(cogeno_opt_extensions "--extensions" ${extensions})
    else()
        set(cogeno_opt_extensions)
    endif()
    set(cogeno_opt_extensions ${cogeno_opt_extensions} PARENT_SCOPE)

    # --modules
    cogeno_get_property(modules MODULES)
    if(DEFINED modules AND modules)
        set(cogeno_opt_modules "--modules" ${modules})
    else()
        set(cogeno_opt_modules)
    endif()
    set(cogeno_opt_modules ${cogeno_opt_modules} PARENT_SCOPE)

    # --templates
    cogeno_get_property(templates TEMPLATES)
    if(DEFINED templates AND templates)
        set(cogeno_opt_templates "--templates" ${templates})
    else()
        set(cogeno_opt_templates)
    endif()
    set(cogeno_opt_templates ${cogeno_opt_templates} PARENT_SCOPE)

    # --cmake:define
    cogeno_get_property(cmake_defines CMAKE_DEFINES)
    list(REMOVE_DUPLICATES cmake_defines)
    # Add current values
    foreach(cmake_define ${cmake_defines})
        if(DEFINED ${cmake_define})
            list(APPEND cogeno_opt_cmake_defines
                        --cmake:define "\"${cmake_define}=${${cmake_define}}\"")
        endif()
    endforeach()
    set(cogeno_opt_cmake_defines ${cogeno_opt_cmake_defines} PARENT_SCOPE)

    # --cmake:cache
    cogeno_get_property(cmake_cache CMAKE_CACHE)
    if(DEFINED cmake_cache)
        set(cogeno_opt_cmake_cache "--cmake:cache" "${cmake_cache}")
    else()
        set(cogeno_opt_cmake_cache)
    endif()
    set(cogeno_opt_cmake_cache ${cogeno_opt_cmake_cache} PARENT_SCOPE)

    # --config:db
    cogeno_get_property(config_db CONFIG_DB)
    if(DEFINED config_db)
        set(cogeno_opt_config_db "--config:db" "${config_db}")
    else()
        set(cogeno_opt_config_db)
    endif()
    set(cogeno_opt_config_db ${cogeno_opt_config_db} PARENT_SCOPE)

    # --config:file
    cogeno_get_property(config_file CONFIG_FILE)
    if(DEFINED config_file)
        set(cogeno_opt_config_file "--config:file" "${config_file}")
    else()
        set(cogeno_opt_config_file)
    endif()
    set(cogeno_opt_config_file ${cogeno_opt_config_file} PARENT_SCOPE)

    # --config:inputs
    cogeno_get_property(config_inputs CONFIG_INPUTS)
    if(DEFINED config_inputs)
        set(cogeno_opt_config_inputs "--config:inputs" ${config_inputs})
    else()
        set(cogeno_opt_config_inputs)
    endif()
    set(cogeno_opt_config_inputs ${cogeno_opt_config_inputs} PARENT_SCOPE)

    # --config:kconfig-file
    cogeno_get_property(config_kconfig_file CONFIG_KCONFIG_FILE)
    if(DEFINED config_kconfig_file)
        set(cogeno_opt_config_kconfig_file "--config:kconfig-file" "${config_kconfig_file}")
    else()
        set(cogeno_opt_config_kconfig_file)
    endif()
    set(cogeno_opt_config_kconfig_file ${cogeno_opt_config_kconfig_file} PARENT_SCOPE)

    # --config:kconfig-srctree
    cogeno_get_property(config_kconfig_srctree CONFIG_KCONFIG_SRCTREE)
    if(DEFINED config_kconfig_srctree)
        set(cogeno_opt_config_kconfig_srctree "--config:kconfig-srctree" "${config_kconfig_srctree}")
    else()
        set(cogeno_opt_config_kconfig_srctree)
    endif()
    set(cogeno_opt_config_kconfig_srctree ${cogeno_opt_config_kconfig_srctree} PARENT_SCOPE)

    # --config:kconfig-defines
    cogeno_get_property(config_kconfig_defines CONFIG_KCONFIG_DEFINES)
    if(DEFINED config_kconfig_defines)
        set(cogeno_opt_config_kconfig_defines "--config:kconfig-defines")
        # Add current values
        foreach(config_kconfig_define ${config_kconfig_defines})
            list(APPEND cogeno_opt_config_kconfig_defines "\"${config_kconfig_define}\"")
        endforeach()
    else()
        set(cogeno_opt_config_kconfig_defines)
    endif()
    set(cogeno_opt_config_kconfig_defines ${cogeno_opt_config_kconfig_defines} PARENT_SCOPE)

    # --edts:dts
    cogeno_get_property(edts_dts EDTS_DTS)
    if(DEFINED edts_dts)
        set(cogeno_opt_edts_dts "--edts:dts" "${edts_dts}")
    else()
        set(cogeno_opt_edts_dts)
    endif()
    set(cogeno_opt_edts_dts ${cogeno_opt_edts_dts} PARENT_SCOPE)

    # --edts:dts-pp-sources
    cogeno_get_property(edts_dts_pp_sources EDTS_DTS_PP_SOURCES)
    if(DEFINED edts_dts_pp_sources)
        set(cogeno_opt_edts_dts_pp_sources "--edts:dts-pp-sources" ${edts_dts_pp_sources})
    else()
        set(cogeno_opt_edts_dts_pp_sources)
    endif()
    set(cogeno_opt_edts_dts_pp_sources ${cogeno_opt_edts_dts_pp_sources} PARENT_SCOPE)

    # --edts:dts-pp-defines
    cogeno_get_property(edts_dts_pp_defines EDTS_DTS_PP_DEFINES)
    if(DEFINED edts_dts_pp_defines)
        set(cogeno_opt_edts_dts_pp_defines "--edts:dts-pp-defines")
        # Add current values
        foreach(edts_dts_pp_define ${edts_dts_pp_defines})
            list(APPEND cogeno_opt_edts_dts_pp_defines "\"${edts_dts_pp_define}\"")
        endforeach()
    else()
        set(cogeno_opt_edts_dts_pp_defines)
    endif()
    set(cogeno_opt_edts_dts_pp_defines ${cogeno_opt_edts_dts_pp_defines} PARENT_SCOPE)

    # --edts:dts-pp-include-dirs
    cogeno_get_property(edts_dts_pp_include_dirs EDTS_DTS_PP_INCLUDE_DIRS)
    if(DEFINED edts_dts_pp_include_dirs)
        set(cogeno_opt_edts_dts_pp_include_dirs "--edts:dts-pp-include-dirs" ${edts_dts_pp_include_dirs})
    else()
        set(cogeno_opt_edts_dts_pp_include_dirs)
    endif()
    set(cogeno_opt_edts_dts_pp_include_dirs ${cogeno_opt_edts_dts_pp_include_dirs} PARENT_SCOPE)

    # --edts:bindings-dirs
    cogeno_get_property(edts_bindings_dirs EDTS_BINDINGS_DIRS)
    if(DEFINED edts_bindings_dirs)
        set(cogeno_opt_edts_bindings_dirs "--edts:bindings-dirs" ${edts_bindings_dirs})
    else()
        set(cogeno_opt_edts_bindings_dirs)
    endif()
    set(cogeno_opt_edts_bindings_dirs ${cogeno_opt_edts_bindings_dirs} PARENT_SCOPE)

    # --edts:bindings-exclude
    cogeno_get_property(edts_bindings_exclude EDTS_BINDINGS_EXCLUDE)
    if(DEFINED edts_bindings_exclude)
        set(cogeno_opt_edts_bindings_exclude "--edts:bindings-exclude" ${edts_bindings_exclude})
    else()
        set(cogeno_opt_edts_bindings_exclude)
    endif()
    set(cogeno_opt_edts_bindings_exclude ${cogeno_opt_edts_bindings_exclude} PARENT_SCOPE)

    # --edts:bindings-no-default
    cogeno_get_property(edts_bindings_no_default EDTS_BINDINGS_NO_DEFAULT)
    if(DEFINED edts_bindings_no_default)
        set(cogeno_opt_edts_bindings_no_default "--edts:bindings-no-default" ${edts_bindings_no_default})
    else()
        set(cogeno_opt_edts_bindings_no_default)
    endif()
    set(cogeno_opt_edts_bindings_no_default ${cogeno_opt_edts_bindings_no_default} PARENT_SCOPE)

    # --edts:db
    cogeno_get_property(edts_db EDTS_DB)
    if(DEFINED edts_db)
        set(cogeno_opt_edts_db "--edts:db" "${edts_db}")
    else()
        set(cogeno_opt_edts_db)
    endif()
    set(cogeno_opt_edts_db ${cogeno_opt_edts_db} PARENT_SCOPE)

    # --protobuf:db-dir
    cogeno_get_property(protobuf_db_dir PROTOBUF_DB_DIR)
    if(DEFINED protobuf_db_dir)
        set(cogeno_opt_protobuf_db_dir "--protobuf:db-dir" "${protobuf_db_dir}")
    else()
        set(cogeno_opt_protobuf_db_dir)
    endif()
    set(cogeno_opt_protobuf_db_dir ${cogeno_opt_protobuf_db_dir} PARENT_SCOPE)

    # Search for protobuf dependencies
    set(args_protobuf_sources)
    set(args_protobuf_include_dirs)
    foreach(depend ${args_depends})
        if(TARGET ${depend})
            continue()
        endif()
        # Find path of dependency
        if(NOT IS_ABSOLUTE ${depend})
            # relative path - use whole relative path
            set(depend ${CMAKE_CURRENT_SOURCE_DIR}/${depend})
        endif()
        if(IS_DIRECTORY "${depend}")
            FILE(GLOB protos LIST_DIRECTORIES false "${depend}/*.proto")
            if(protos)
                list(APPEND args_protobuf_include_dirs ${depend})
            endif()
        else()
            get_filename_component(depend_ext ${depend} EXT)
            if("${depend_ext}" STREQUAL ".proto")
                list(APPEND args_protobuf_sources ${depend})
                get_filename_component(depend_dir ${depend} DIRECTORY)
                list(APPEND args_protobuf_include_dirs ${depend_dir})
            endif()
        endif()
    endforeach()

    # --protobuf:include-dirs
    cogeno_get_property(protobuf_include_dirs PROTOBUF_INCLUDE_DIRS)
    if(NOT DEFINED protobuf_include_dirs)
        set(protobuf_include_dirs ${args_protobuf_include_dirs})
    else()
        set(protobuf_include_dirs ${protobuf_include_dirs} ${args_protobuf_include_dirs})
    endif()
    if(protobuf_include_dirs)
        set(cogeno_opt_protobuf_include_dirs "--protobuf:include-dirs" ${protobuf_include_dirs})
    else()
        set(cogeno_opt_protobuf_include_dirs)
    endif()
    set(cogeno_opt_protobuf_include_dirs ${cogeno_opt_protobuf_include_dirs} PARENT_SCOPE)

    # --protobuf:sources
    cogeno_get_property(protobuf_sources PROTOBUF_SOURCES)
    if(NOT DEFINED protobuf_sources)
        set(protobuf_sources ${args_protobuf_sources})
    else()
        set(protobuf_sources ${protobuf_sources} ${args_protobuf_sources})
    endif()
    if(protobuf_sources)
        set(cogeno_opt_protobuf_sources "--protobuf:sources" ${protobuf_sources})
    else()
        set(cogeno_opt_protobuf_sources)
    endif()
    set(cogeno_opt_protobuf_sources ${cogeno_opt_protobuf_sources} PARENT_SCOPE)
endfunction()

# Get all the files that make up cogeno for dependency reasons.
#file(GLOB_RECURSE cogeno_sources LIST_DIRECTORIES false
#      ${COGENO_BASE}/cogeno/*.py
#      ${COGENO_BASE}/cogeno/*.yaml
#      ${COGENO_BASE}/cogeno/*.c
#      ${COGENO_BASE}/cogeno/*.jinja2)


function(cogeno_sources
    target          # The CMake target that depends on the generated file
    )
    # Prepare arguments
    set(options EXTERN DELETE_CODE)
    set(oneValueArgs SOURCE_DIR INCLUDE_DIR TXTFILE_DIR)
    set(multiValueArgs INCLUDES TXTFILES COGENO_DEFINES DEPENDS)
    cmake_parse_arguments(SOURCES "${options}" "${oneValueArgs}"
                          "${multiValueArgs}" ${ARGN})

    # Prepare all options
    cogeno_set_options("${SOURCES_SOURCE_DIR}" "${SOURCES_INCLUDE_DIR}" "${SOURCES_TXTFILE_DIR}"
                       "${SOURCES_DELETE_CODE}" "${SOURCES_COGENO_DEFINES}" "${SOURCES_DEPENDS}")

    message(STATUS "Cogeno will generate for target ${target}")
    set(include_file FALSE)
    set(text_file FALSE)
    foreach(arg ${SOURCES_UNPARSED_ARGUMENTS} "!includes!" ${SOURCES_INCLUDES} "!txtfiles!" ${SOURCES_TXTFILES})
        if("${arg}" STREQUAL "!includes!")
            # We are now processing include files
            set(text_file FALSE)
            set(include_file TRUE)
            continue()
        endif()
        if("${arg}" STREQUAL "!txtfiles!")
            # We are now processing text files
            set(text_file TRUE)
            set(include_file FALSE)
            continue()
        endif()
        get_filename_component(generated_file_name ${arg} NAME)

        # Find path for generated file
        if(${include_file})
            # This is an include file - we got an output directory for include files
            # -> put into include directory
            set(generated_file ${cogeno_opt_include_dir}/${generated_file_name})
            set(generated_dir ${cogeno_opt_include_dir})
        elseif(${text_file})
            # This is a text file - we got an output directory for text files
            # -> put into txtfiles directory
            set(generated_file ${cogeno_opt_txtfile_dir}/${generated_file_name})
            set(generated_dir ${cogeno_opt_txtfile_dir})
        else()
            # This is a source file - we got an output directory for source files
            # -> put into source directory but take care that
            #    compilable source files must be generated to the current binary directory.
            #    Otherwise this would trigger CMake issue #14633:
            #    https://gitlab.kitware.com/cmake/cmake/issues/14633
            get_filename_component(generated_ext ${generated_file_name} EXT)
            if(("${generated_ext}" STREQUAL ".c"
                OR "${generated_ext}" STREQUAL ".cpp")
               AND NOT "${cogeno_opt_source_dir}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
                message(WARNING "Compilable source ${arg} generated to non CMAKE_CURRENT_BINARY_DIR (${cogeno_opt_source_dir})")
            endif()
            set(generated_file ${cogeno_opt_source_dir}/${generated_file_name})
            set(generated_dir ${cogeno_opt_source_dir})
        endif()

        # Find path of template file
        if(IS_ABSOLUTE ${arg})
            set(template_file ${arg})
        else()
            # relative path - use whole relative path
            set(template_file ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
        endif()
        get_filename_component(template_dir ${template_file} DIRECTORY)

        if(IS_DIRECTORY ${template_file})
            message(FATAL_ERROR "cogeno_sources() was called on a directory")
        endif()

        # Remove common template extensions from generated file name
        get_filename_component(generated_ext ${generated_file} EXT)
        get_filename_component(generated_name_we ${generated_file} NAME_WE)
        foreach(gen_ext ".in" ".cogeno" ".py" ".jinja")
            foreach(ext ".h" ".hpp" ".c" ".cpp" ".html" ".txt" ".rst" ".md")
                if("${generated_ext}" STREQUAL "${gen_ext}${ext}")
                    set(generated_file "${generated_dir}/${generated_name_we}${ext}")
                    set(generated_ext ${ext})
                    break()
                endif()
            endforeach()
        endforeach()

        # Generate file from template
        message(STATUS " from '${template_file}'")
        message(STATUS " to   '${generated_file}'")
        add_custom_command(
            COMMENT "cogeno ${generated_file}"
            OUTPUT ${generated_file}
            MAIN_DEPENDENCY ${template_file}
            DEPENDS
            "${cogeno_opt_base}/cogeno/cogeno.py"
            ${cogeno_opt_depends}
            COMMAND
            ${cogeno_opt_executable}
            ${cogeno_opt_executable_args}
            ${cogeno_opt_defines}
            ${cogeno_opt_delete_code}
            ${cogeno_opt_extensions}
            ${cogeno_opt_cmake_defines}
            ${cogeno_opt_cmake_cache}
            ${cogeno_opt_config_db}
            ${cogeno_opt_config_file}
            ${cogeno_opt_config_inputs}
            ${cogeno_opt_config_kconfig_file}
            ${cogeno_opt_config_kconfig_srctree}
            ${cogeno_opt_config_kconfig_defines}
            ${cogeno_opt_edts_bindings_dirs}
            ${cogeno_opt_edts_bindings_exclude}
            ${cogeno_opt_edts_bindings_no_default}
            ${cogeno_opt_edts_db}
            ${cogeno_opt_edts_dts}
            ${cogeno_opt_edts_dts_pp_defines}
            ${cogeno_opt_edts_dts_pp_sources}
            ${cogeno_opt_edts_dts_pp_include_dirs}
            ${cogeno_opt_protobuf_db_dir}
            ${cogeno_opt_protobuf_include_dirs}
            ${cogeno_opt_protobuf_sources}
            ${cogeno_opt_modules}
            ${cogeno_opt_templates}
            ${cogeno_opt_log}
            ${cogeno_opt_lock}
            --input "${template_file}"
            --output "${generated_file}"
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        )

        set_source_files_properties(${generated_file} PROPERTIES GENERATED 1)

        # 1) Add generated files to target component.
        # 2) Add template directory to include path to allow includes with
        #    relative path in generated file to work
        # 3) Add directory of generated file to include path to allow includes
        #    of generated header file with relative path.
        #
        # Standard way to add to target is using CMake target_sources() and
        # target_include_directories().
        #
        # Some platforms need a different strategy to achieve the above 1,2,3.
        if(DEFINED ESP_PLATFORM AND ESP_PLATFORM AND "${target}" STREQUAL "${COMPONENT_NAME}")
            # ESP platforms use idf_component_register
            cogeno_get_property(component_name COMPONENT_NAME)
            if(NOT "${component_name}" STREQUAL "${COMPONENT_NAME}")
                # We are working on a new component - assure everything is prepared
                cogeno_set_property(COMPONENT_SRCS "")
                cogeno_set_property(COMPONENT_INCLUDE_DIRS "")
                cogeno_set_property(COMPONENT_NAME "${COMPONENT_NAME}")
            endif()
            if(${include_file})
                cogeno_set_property(COMPONENT_INCLUDE_DIRS ${generated_dir} APPEND)
                # We add include file to SRCS to get a dependency
                cogeno_set_property(COMPONENT_SRCS ${generated_file} APPEND)
            else()
                cogeno_set_property(COMPONENT_SRCS ${generated_file} APPEND)
            endif()
            cogeno_set_property(COMPONENT_INCLUDE_DIRS ${template_dir} APPEND)
            # Return the values to be used by idf_component_register
            cogeno_get_property(component_srcs COMPONENT_SRCS)
            set(COGENO_COMPONENT_SRCS ${component_srcs} PARENT_SCOPE)
            cogeno_get_property(component_include_dirs COMPONENT_INCLUDE_DIRS)
            list(REMOVE_DUPLICATES component_include_dirs)
            set(COGENO_COMPONENT_INCLUDE_DIRS ${component_include_dirs} PARENT_SCOPE)
        else()
            if(${SOURCES_EXTERN} OR ${text_file})
                # EXTERN:
                # We are adding to a target that was not created in the same CMakelLists.txt file.
                # - The generated property is not visible to the target in this case.
                # - The generated file has to be generated first because of that. target_sources
                #   expects a file in this case.
                # - A dependency has to be added to assure the generated file is build
                #   before the target
                # TXTFILE:
                # Usually text files are added to custom targets.
                # - A dependency has to be added to assure the generated file is build
                #   before the (custom) target
                cogeno_unique_target_name_from_filename(${generated_file} generated_target_name)
                add_custom_target(${generated_target_name} ALL DEPENDS ${generated_file})
                add_dependencies(${target} ${generated_target_name})
                if(NOT ${include_file} AND NOT ${text_file})
                    # We are adding a source file
                    # Assure the ouput directory exists
                    file(MAKE_DIRECTORY ${generated_dir})
                    # Generate the file - add_custom_target will always force (re-)generation
                    file(TOUCH ${generated_file})
                endif()
            endif()
            if(${include_file})
                # Add output directory for generated file to include path to allow includes
                # of generated header file with relative path.
                target_include_directories(${target} SYSTEM BEFORE INTERFACE ${generated_dir})
            elseif(${text_file})
                # Nothing to do
            else()
                target_sources(${target} PRIVATE ${generated_file})
            endif()
            # Add template directory to include path to allow includes with
            # relative path in generated file to work
            if(${include_file})
                # target maybe interface only target
                target_include_directories(${target} PRIVATE INTERFACE ${template_dir})
            elseif(${text_file})
                # Nothing to do
            else()
                target_include_directories(${target} PRIVATE ${template_dir})
            endif()
        endif()

    endforeach()
endfunction()


# Assure cogeno is available
# --------------------------
FindCogeno()
if(NOT Cogeno_FOUND)
    message(FATAL "Cogeno not found")
endif()


# This is a Zephyr project.
# -------------------------
if(DEFINED ZEPHYR_BASE AND EXISTS "${ZEPHYR_BASE}")
    message(STATUS "Cogeno added to Zephyr")

    function(zephyr_sources_cogeno)
        cogeno_sources(zephyr EXTERN ${ARGN})
    endfunction()

    function(zephyr_sources_cogeno_ifdef feature_toggle)
        if(${${feature_toggle}})
            zephyr_sources_cogeno(${ARGN})
        endif()
    endfunction()

    function(zephyr_library_sources_cogeno)
        cogeno_sources(${ZEPHYR_CURRENT_LIBRARY} EXTERN ${ARGN})
    endfunction()

    function(zephyr_library_sources_cogeno_ifdef feature_toggle)
        if(${${feature_toggle}})
            zephyr_library_sources_cogeno(${ARGN})
        endif()
    endfunction()

    function(zephyr_library_includes_cogeno)
        cogeno_sources(zephyr_interface EXTERN INCLUDES ${ARGN}
                       INCLUDE_DIR "${CMAKE_BINARY_DIR}/zephyr/include/generated")
    endfunction()

    function(zephyr_library_includes_cogeno_ifdef feature_toggle)
        if(${${feature_toggle}})
            zephyr_library_includes_cogeno(${ARGN})
        endif()
    endfunction()

    # Add include directory to the directories scanned for syscall include files
    # - workaround
    macro(zephyr_syscall_include_dirs)
        foreach(dir ${ARGN})
            get_filename_component(dir "${dir}" REALPATH)
            if(${dir} IN_LIST SYSCALL_INCLUDE_DIRS)
                continue()
            endif()
            list(APPEND SYSCALL_INCLUDE_DIRS ${dir})
        endforeach()
        SET(SYSCALL_INCLUDE_DIRS  "${SYSCALL_INCLUDE_DIRS}" CACHE INTERNAL "SYSCALL_INCLUDE_DIRS")
    endmacro()

# This is an ESP IDF project
# --------------------------
elseif(DEFINED ESP_PLATFORM AND ESP_PLATFORM)
    message(STATUS "Cogeno added to ESP platform")

# Could not identify the type of project
# --------------------------------------
else()
    message(STATUS "Cogeno added to unknown platform")

endif()