#
# Create functions - start
#

function(create_system)
  cmake_parse_arguments(OBJECT "" "ENTRY;FORMAT;NAME;OBJECT" "" ${ARGN})

  set_property(GLOBAL PROPERTY SYSTEM_${OBJECT_NAME}          TRUE)
  set_property(GLOBAL PROPERTY SYSTEM_${OBJECT_NAME}_OBJ_TYPE SYSTEM)
  set_property(GLOBAL PROPERTY SYSTEM_${OBJECT_NAME}_NAME     ${OBJECT_NAME})
  set_property(GLOBAL PROPERTY SYSTEM_${OBJECT_NAME}_FORMAT   ${OBJECT_FORMAT})
  set_property(GLOBAL PROPERTY SYSTEM_${OBJECT_NAME}_ENTRY    ${OBJECT_ENTRY})

  set(${OBJECT_OBJECT} SYSTEM_${OBJECT_NAME} PARENT_SCOPE)
endfunction()

function(create_region)
  cmake_parse_arguments(OBJECT "" "NAME;OBJECT;SIZE;START;FLAGS" "" ${ARGN})

  if(DEFINED OBJECT_SIZE)
    if(${OBJECT_SIZE} MATCHES "^([0-9]*)[kK]$")
      math(EXPR OBJECT_SIZE "1024 * ${CMAKE_MATCH_1}" OUTPUT_FORMAT HEXADECIMAL)
    elseif(${OBJECT_SIZE} MATCHES "^([0-9]*)[mM]$")
      math(EXPR OBJECT_SIZE "1024 * 1024 * ${CMAKE_MATCH_1}" OUTPUT_FORMAT HEXADECIMAL)
    elseif(NOT (${OBJECT_SIZE} MATCHES "^([0-9]*)$" OR ${OBJECT_SIZE} MATCHES "^0x([0-9a-fA-F]*)$"))
      message(FATAL_ERROR "SIZE format is unknown.")
    endif()
  endif()

  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}          TRUE)
  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}_OBJ_TYPE REGION)
  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}_NAME     ${OBJECT_NAME})
  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}_ADDRESS  ${OBJECT_START})
  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}_FLAGS    ${OBJECT_FLAGS})
  set_property(GLOBAL PROPERTY REGION_${OBJECT_NAME}_SIZE     ${OBJECT_SIZE})

  set(${OBJECT_OBJECT} REGION_${OBJECT_NAME} PARENT_SCOPE)
endfunction()

function(get_parent)
  cmake_parse_arguments(GET_PARENT "" "OBJECT;PARENT;TYPE" "" ${ARGN})

  get_property(type GLOBAL PROPERTY ${GET_PARENT_OBJECT}_OBJ_TYPE)
  if(${type} STREQUAL ${GET_PARENT_TYPE})
    # Already the right type, so just set and return.
    set(${GET_PARENT_PARENT} ${GET_PARENT_OBJECT} PARENT_SCOPE)
    return()
  endif()

  get_property(parent GLOBAL PROPERTY ${GET_PARENT_OBJECT}_PARENT)
  get_property(type   GLOBAL PROPERTY ${parent}_OBJ_TYPE)
  while(NOT ${type} STREQUAL ${GET_PARENT_TYPE})
    get_property(parent GLOBAL PROPERTY ${parent}_PARENT)
    get_property(type   GLOBAL PROPERTY ${parent}_OBJ_TYPE)
  endwhile()

  set(${GET_PARENT_PARENT} ${parent} PARENT_SCOPE)
endfunction()

function(create_group)
  cmake_parse_arguments(OBJECT "" "GROUP;LMA;NAME;OBJECT;SYMBOL;VMA" "" ${ARGN})

  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}          TRUE)
  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_OBJ_TYPE GROUP)
  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_NAME     ${OBJECT_NAME})
  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_SYMBOL   ${OBJECT_SYMBOL})

  if(DEFINED OBJECT_GROUP)
    find_object(OBJECT parent NAME ${OBJECT_GROUP})
  else()
    if(DEFINED OBJECT_VMA)
      find_object(OBJECT obj NAME ${OBJECT_VMA})
      get_parent(OBJECT ${obj} PARENT parent TYPE REGION)

      get_property(vma GLOBAL PROPERTY ${parent}_NAME)
      set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_VMA ${vma})
    endif()

    if(DEFINED OBJECT_LMA)
      find_object(OBJECT obj NAME ${OBJECT_LMA})
      get_parent(OBJECT ${obj} PARENT parent TYPE REGION)

      get_property(lma GLOBAL PROPERTY ${parent}_NAME)
      set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_LMA ${lma})
    endif()
  endif()

  get_property(GROUP_FLAGS_INHERITED GLOBAL PROPERTY ${parent}_FLAGS)
  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_FLAGS  ${GROUP_FLAGS_INHERITED})
  set_property(GLOBAL PROPERTY GROUP_${OBJECT_NAME}_PARENT ${parent})

  add_group(OBJECT ${parent} GROUP GROUP_${OBJECT_NAME})

  set(${OBJECT_OBJECT} GROUP_${OBJECT_NAME} PARENT_SCOPE)
endfunction()

function(create_section)
  set(single_args "NAME;ADDRESS;ALIGN_WITH_INPUT;TYPE;ALIGN;ENDALIGN;SUBALIGN;VMA;LMA;NOINPUT;NOINIT;NOSYMBOLS;GROUP;SYSTEM")
  set(multi_args  "PASS")

  cmake_parse_arguments(SECTION "" "${single_args}" "${multi_args}" ${ARGN})

  if(DEFINED SECTION_PASS AND NOT "${PASS}" IN_LIST SECTION_PASS)
    # This section is not active in this pass, ignore.
    return()
  endif()

  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME} TRUE)
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_OBJ_TYPE         SECTION)
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_NAME             ${SECTION_NAME})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_ADDRESS          ${SECTION_ADDRESS})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_TYPE             ${SECTION_TYPE})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_ALIGN            ${SECTION_ALIGN})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_ALIGN_WITH_INPUT ${SECTION_ALIGN_WITH_INPUT})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_SUBALIGN         ${SECTION_SUBALIGN})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_ENDALIGN         ${SECTION_ENDALIGN})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_NOINPUT          ${SECTION_NOINPUT})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_NOINIT           ${SECTION_NOINIT})
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_NOSYMBOLS        ${SECTION_NOSYMBOLS})

  string(REGEX REPLACE "^[\.]" "" name_clean "${SECTION_NAME}")
  string(REPLACE "." "_" name_clean "${name_clean}")
  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_NAME_CLEAN ${name_clean})

  set_property(GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_start      ${name_clean})
  set_property(GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_size       ${name_clean})
  set_property(GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_load_start ${name_clean})
  set_property(GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_end        ${name_clean})

  set(INDEX 100)
  set(settings_single "ALIGN;ANY;FIRST;KEEP;OFFSET;PASS;PRIO;SECTION;SORT")
  set(settings_multi  "FLAGS;INPUT;SYMBOLS")
  foreach(settings ${SECTION_SETTINGS})
    if("${settings}" MATCHES "^{(.*)}$")
      cmake_parse_arguments(SETTINGS "" "${settings_single}" "${settings_multi}" ${CMAKE_MATCH_1})

      if(NOT ("${SETTINGS_SECTION}" STREQUAL "${SECTION_NAME}"))
        continue()
      endif()

      if(DEFINED SETTINGS_PASS AND NOT "${PASS}" IN_LIST SETTINGS_PASS)
        # This section setting is not active in this pass, ignore.
        continue()
      endif()

      if(DEFINED SETTINGS_PRIO)
        set(idx ${SETTINGS_PRIO})
      else()
        set(idx ${INDEX})
        math(EXPR INDEX "${INDEX} + 1")
      endif()

      foreach(setting ${settings_single} ${settings_multi})
        set_property(GLOBAL PROPERTY
          SECTION_${SECTION_NAME}_SETTING_${idx}_${setting}
          ${SETTINGS_${setting}}
        )
        if(DEFINED SETTINGS_SORT)
          set_property(GLOBAL PROPERTY SYMBOL_TABLE___${name_clean}_end ${name_clean}_end)
        endif()
      endforeach()

      set_property(GLOBAL APPEND PROPERTY SECTION_${SECTION_NAME}_SETTINGS_INDICIES ${idx})

    endif()
  endforeach()

  get_property(indicies GLOBAL PROPERTY SECTION_${SECTION_NAME}_SETTINGS_INDICIES)
  if(DEFINED indicies)
    list(SORT indicies COMPARE NATURAL)
    set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_SETTINGS_INDICIES ${indicies})
  endif()

  if(DEFINED SECTION_GROUP)
    find_object(OBJECT parent NAME ${SECTION_GROUP})
  elseif(DEFINED SECTION_VMA OR DEFINED SECTION_LMA)
    if(DEFINED SECTION_VMA)
      find_object(OBJECT object NAME ${SECTION_VMA})
      get_parent(OBJECT ${object} PARENT parent TYPE REGION)

      get_property(vma GLOBAL PROPERTY ${parent}_NAME)
      set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_VMA ${vma})
      set(SECTION_VMA ${vma})
    endif()

    if(DEFINED SECTION_LMA)
      find_object(OBJECT object NAME ${SECTION_LMA})
      get_parent(OBJECT ${object} PARENT parent TYPE REGION)

      get_property(lma GLOBAL PROPERTY ${parent}_NAME)
      set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_LMA ${lma})
      set(SECTION_LMA ${lma})
    endif()
  else()
    set(parent ${SECTION_SYSTEM})
  endif()

  set_property(GLOBAL PROPERTY SECTION_${SECTION_NAME}_PARENT ${parent})
  add_section(OBJECT ${parent} SECTION ${SECTION_NAME} ADDRESS ${SECTION_ADDRESS} VMA ${SECTION_VMA})
endfunction()

function(create_symbol)
  cmake_parse_arguments(SYM "" "OBJECT;EXPR;SIZE;SUBALIGN;SYMBOL" "" ${ARGN})

  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL} TRUE)
  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL}_OBJ_TYPE SYMBOL)
  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL}_NAME     ${SYM_SYMBOL})
  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL}_EXPR     ${SYM_EXPR})
  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL}_SIZE     ${SYM_SIZE})
  set_property(GLOBAL PROPERTY SYMBOL_${SYM_SYMBOL}_SYMBOL   ${SYM_SYMBOL})

  set_property(GLOBAL PROPERTY SYMBOL_TABLE_${SYM_SYMBOL} ${SYM_SYMBOL})

  add_symbol(OBJECT ${SYM_OBJECT} SYMBOL SYMBOL_${SYM_SYMBOL})
endfunction()

#
# Create functions - end
#

#
# Add functions - start
#
function(add_region)
  cmake_parse_arguments(ADD_REGION "" "OBJECT;REGION" "" ${ARGN})

  get_property(exists GLOBAL PROPERTY ${ADD_REGION_OBJECT})
  if(NOT exists)
    message(FATAL_ERROR
      "Adding region ${ADD_REGION_REGION} to none-existing object: "
      "${ADD_REGION_OBJECT}"
    )
  endif()

  set_property(GLOBAL PROPERTY ${ADD_REGION_REGION}_PARENT ${ADD_REGION_OBJECT})
  set_property(GLOBAL APPEND PROPERTY ${ADD_REGION_OBJECT}_REGIONS ${ADD_REGION_REGION})
endfunction()

function(add_group)
  cmake_parse_arguments(ADD_GROUP "" "OBJECT;GROUP" "" ${ARGN})

  get_property(exists GLOBAL PROPERTY ${ADD_GROUP_OBJECT})
  if(NOT exists)
    message(FATAL_ERROR
      "Adding group ${ADD_GROUP_GROUP} to none-existing object: "
      "${ADD_GROUP_OBJECT}"
    )
  endif()

  get_property(vma GLOBAL PROPERTY ${ADD_GROUP_GROUP}_VMA)
  get_property(object_name GLOBAL PROPERTY ${ADD_GROUP_OBJECT}_NAME)

  if((NOT DEFINED vma) OR ("${vma}" STREQUAL ${object_name}))
    set_property(GLOBAL APPEND PROPERTY ${ADD_GROUP_OBJECT}_GROUPS ${ADD_GROUP_GROUP})
  else()
    set_property(GLOBAL APPEND PROPERTY ${ADD_GROUP_OBJECT}_${vma}_GROUPS ${ADD_GROUP_GROUP})
  endif()
endfunction()

function(add_section)
  cmake_parse_arguments(ADD_SECTION "" "OBJECT;SECTION;ADDRESS;VMA" "" ${ARGN})

  if(DEFINED ADD_SECTION_OBJECT)
    get_property(type GLOBAL PROPERTY ${ADD_SECTION_OBJECT}_OBJ_TYPE)
    get_property(object_name GLOBAL PROPERTY ${ADD_SECTION_OBJECT}_NAME)

    if(NOT DEFINED type)
      message(FATAL_ERROR
              "Adding section ${ADD_SECTION_SECTION} to "
              "none-existing object: ${ADD_SECTION_OBJECT}"
      )
    endif()
  else()
    set(ADD_SECTION_OBJECT RELOCATEABLE)
  endif()

  if("${ADD_SECTION_VMA}" STREQUAL "${object_name}" AND DEFINED ADD_SECTION_ADDRESS)
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_SECTIONS_FIXED
      SECTION_${ADD_SECTION_SECTION}
    )
  elseif(NOT DEFINED ADD_SECTION_VMA AND DEFINED SECTION_ADDRESS)
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_SECTIONS_FIXED
      SECTION_${ADD_SECTION_SECTION}
    )
  elseif("${ADD_SECTION_VMA}" STREQUAL "${object_name}")
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_SECTIONS
      SECTION_${ADD_SECTION_SECTION}
    )
  elseif(NOT DEFINED ADD_SECTION_VMA)
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_SECTIONS
      SECTION_${ADD_SECTION_SECTION}
    )
  elseif(DEFINED SECTION_ADDRESS)
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_${ADD_SECTION_VMA}_SECTIONS_FIXED
      SECTION_${ADD_SECTION_SECTION}
    )
  else()
    set_property(GLOBAL APPEND PROPERTY
      ${ADD_SECTION_OBJECT}_${ADD_SECTION_VMA}_SECTIONS
      SECTION_${ADD_SECTION_SECTION}
    )
  endif()
endfunction()

function(add_symbol)
  cmake_parse_arguments(ADD_SYMBOL "" "OBJECT;SYMBOL" "" ${ARGN})

  # Section can be fixed address or not, VMA == LMA, .
  #
  get_property(exists GLOBAL PROPERTY ${ADD_SYMBOL_OBJECT})
  if(NOT exists)
    message(FATAL_ERROR
      "Adding symbol ${ADD_SYMBOL_SYMBOL} to none-existing object: "
      "${ADD_SYMBOL_OBJECT}"
    )
  endif()

  set_property(GLOBAL APPEND PROPERTY ${ADD_SYMBOL_OBJECT}_SYMBOLS ${ADD_SYMBOL_SYMBOL})
endfunction()

#
# Add functions - end
#

#
# Retrieval functions - start
#
function(find_object)
  cmake_parse_arguments(FIND "" "OBJECT;NAME" "" ${ARGN})

  get_property(REGION  GLOBAL PROPERTY REGION_${FIND_NAME})
  get_property(GROUP   GLOBAL PROPERTY GROUP_${FIND_NAME})
  get_property(SECTION GLOBAL PROPERTY SECTION_${FIND_NAME})

  if(REGION)
    set(${FIND_OBJECT} REGION_${FIND_NAME} PARENT_SCOPE)
  elseif(GROUP)
    set(${FIND_OBJECT} GROUP_${FIND_NAME} PARENT_SCOPE)
  elseif(SECTION)
    set(${FIND_OBJECT} SECTION_${FIND_NAME} PARENT_SCOPE)
  else()
    message(WARNING "No object with name ${FIND_NAME} could be found.")
  endif()
endfunction()

function(get_objects)
  cmake_parse_arguments(GET "" "LIST;OBJECT;TYPE" "" ${ARGN})

  get_property(type GLOBAL PROPERTY ${GET_OBJECT}_OBJ_TYPE)

  if(${type} STREQUAL SECTION)
    # A section doesn't have sub-items.
    return()
  endif()

  if(NOT (${GET_TYPE} STREQUAL SECTION
     OR   ${GET_TYPE} STREQUAL GROUP)
  )
    message(WARNING "Only retrieval of SECTION GROUP objects are supported.")
    return()
  endif()

  set(out)

  get_parent(OBJECT ${GET_OBJECT} PARENT parent TYPE SYSTEM)
  get_property(regions GLOBAL PROPERTY ${parent}_REGIONS)
  list(REMOVE_ITEM regions ${GET_OBJECT})

  if(${GET_TYPE} STREQUAL SECTION)
    get_property(sections GLOBAL PROPERTY ${GET_OBJECT}_SECTIONS_FIXED)
    list(APPEND out ${sections})

    get_property(groups GLOBAL PROPERTY ${GET_OBJECT}_GROUPS)
    foreach(group ${groups})
      get_objects(LIST sections OBJECT ${group} TYPE ${GET_TYPE})
      list(APPEND out ${sections})
    endforeach()

    get_property(sections GLOBAL PROPERTY ${GET_OBJECT}_SECTIONS)
    list(APPEND out ${sections})

    foreach(region ${regions})
      get_property(vma GLOBAL PROPERTY ${region}_NAME)

      get_property(sections GLOBAL PROPERTY ${GET_OBJECT}_${vma}_SECTIONS_FIXED)
      list(APPEND out ${sections})

      get_property(groups GLOBAL PROPERTY ${GET_OBJECT}_${vma}_GROUPS)
      foreach(group ${groups})
        get_objects(LIST sections OBJECT ${group} TYPE ${GET_TYPE})
        list(APPEND out ${sections})
      endforeach()

      get_property(sections GLOBAL PROPERTY ${GET_OBJECT}_${vma}_SECTIONS)
      list(APPEND out ${sections})
    endforeach()
  endif()

  if(${GET_TYPE} STREQUAL GROUP)
    get_property(groups GLOBAL PROPERTY ${GET_OBJECT}_GROUPS)
    list(APPEND out ${groups})

    foreach(group ${groups})
      get_objects(LIST subgroups OBJECT ${group} TYPE ${GET_TYPE})
      list(APPEND out ${subgroups})
    endforeach()

    foreach(region ${regions})
      get_property(vma GLOBAL PROPERTY ${region}_NAME)

      get_property(groups GLOBAL PROPERTY ${GET_OBJECT}_${vma}_GROUPS)
      list(APPEND out ${groups})

      foreach(group ${groups})
        get_objects(LIST subgroups OBJECT ${group} TYPE ${GET_TYPE})
        list(APPEND out ${subgroups})
      endforeach()
    endforeach()
  endif()

  set(${GET_LIST} ${out} PARENT_SCOPE)
endfunction()

#
# Retrieval functions - end
#

function(is_empty)
  cmake_parse_arguments(IS_EMPTY "" "OBJECT" "" ${ARGN})

  get_property(sections GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_SECTIONS_FIXED)
  if(DEFINED sections)
    set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
    return()
  endif()

  get_property(groups GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_GROUPS)
  if(DEFINED groups)
    set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
    return()
  endif()


  get_property(sections GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_SECTIONS)
  if(DEFINED sections)
    set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
    return()
  endif()

  get_parent(OBJECT ${IS_EMPTY_OBJECT} PARENT parent TYPE SYSTEM)
  get_property(regions GLOBAL PROPERTY ${parent}_REGIONS)
  list(REMOVE_ITEM regions ${IS_EMPTY_OBJECT})
  foreach(region ${regions})
    get_property(vma GLOBAL PROPERTY ${region}_NAME)
    get_property(sections GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_${vma}_SECTIONS_FIXED)
    if(DEFINED sections)
      set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
      return()
    endif()

    get_property(groups GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_${vma}_GROUPS)
    if(DEFINED groups)
      set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
      return()
    endif()

    get_property(sections GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_${vma}_SECTIONS)
    if(DEFINED sections)
      set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY FALSE)
      return()
    endif()
  endforeach()
  set_property(GLOBAL PROPERTY ${IS_EMPTY_OBJECT}_EMPTY TRUE)
endfunction()

# This function post process the region for easier use.
#
# This is common post processing.
# If the calling <linker>_script.cmake generator implements its own
# process_region(), then the process_region_common() must be called explicitly
# from the process_region() from the <linker>_script.cmake generator.
#
# This allows a custom <linker>_script.cmake generator to completely disable
# the common post processing of regions.
#
# Tasks:
# - Apply missing settings, such as initial address for first section in a region.
# - Symbol names on sections
# - Ordered list of all sections for easier retrival on printing and configuration.
function(process_region_common)
  cmake_parse_arguments(REGION_COMMON "" "OBJECT" "" ${ARGN})

  is_empty(OBJECT ${REGION_COMMON_OBJECT})

  set(sections)
  get_objects(LIST sections OBJECT ${REGION_COMMON_OBJECT} TYPE SECTION)
  set_property(GLOBAL PROPERTY ${REGION_COMMON_OBJECT}_SECTION_LIST_ORDERED ${sections})

  set(groups)
  get_objects(LIST groups OBJECT ${REGION_COMMON_OBJECT} TYPE GROUP)
  set_property(GLOBAL PROPERTY ${REGION_COMMON_OBJECT}_GROUP_LIST_ORDERED ${groups})

  list(LENGTH sections section_count)
  if(section_count GREATER 0)
    list(GET sections 0 section)
    get_property(address GLOBAL PROPERTY ${section}_ADDRESS)
    if(NOT DEFINED address)
      get_parent(OBJECT ${REGION_COMMON_OBJECT} PARENT parent TYPE REGION)
      get_property(address GLOBAL PROPERTY ${parent}_ADDRESS)
      set_property(GLOBAL PROPERTY ${section}_ADDRESS ${address})
    endif()
  endif()

  get_parent(OBJECT ${REGION_COMMON_OBJECT} PARENT parent TYPE SYSTEM)
  get_property(regions GLOBAL PROPERTY ${parent}_REGIONS)
  list(REMOVE_ITEM regions ${REGION_COMMON_OBJECT})
  foreach(region ${regions})
    get_property(vma GLOBAL PROPERTY ${region}_NAME)
    set(sections_${vma})
    get_property(sections GLOBAL PROPERTY ${REGION_COMMON_OBJECT}_${vma}_SECTIONS_FIXED)
    list(APPEND sections_${vma} ${sections})

    get_property(groups GLOBAL PROPERTY ${REGION_COMMON_OBJECT}_${vma}_GROUPS)
    foreach(group ${groups})
      get_objects(LIST sections OBJECT ${group} TYPE SECTION)
      list(APPEND sections_${vma} ${sections})
    endforeach()

    get_property(sections GLOBAL PROPERTY ${REGION_COMMON_OBJECT}_${vma}_SECTIONS)
    list(APPEND sections_${vma} ${sections})

    list(LENGTH sections_${vma} section_count)
    if(section_count GREATER 0)
      list(GET sections_${vma} 0 section)
      get_property(address GLOBAL PROPERTY ${section}_ADDRESS)
      if(NOT DEFINED address)
        get_property(address GLOBAL PROPERTY ${region}_ADDRESS)
        set_property(GLOBAL PROPERTY ${section}_ADDRESS ${address})
      endif()
    endif()
  endforeach()
endfunction()

if(NOT COMMAND process_region)
  function(process_region)
    process_region_common(${ARGN})
  endfunction()
endif()


#
# String functions - start
#
# Each linker must implement their own <type>_to_string() functions to
# generate a correct linker script.
#
if(NOT COMMAND system_to_string)
  function(system_to_string)
    message(WARNING "No linker defined function found. Please implement a "
                    "system_to_string() function for this linker."
    )
  endfunction()
endif()

if(NOT COMMAND group_to_string)
  function(group_to_string)
    message(WARNING "No linker defined function found. Please implement a "
                    "group_to_string() function for this linker."
    )
  endfunction()
endif()

if(NOT COMMAND section_to_string)
  function(section_to_string)
    message(WARNING "No linker defined function found. Please implement a "
                    "section_to_string() function for this linker."
    )
  endfunction()
endif()

if(NOT COMMAND symbol_to_string)
  function(symbol_to_string)
    message(WARNING "No linker defined function found. Please implement a "
                    "symbol_to_string() function for this linker."
    )
  endfunction()
endif()

function(to_string)
  cmake_parse_arguments(STRING "" "OBJECT;STRING" "" ${ARGN})

  get_property(type GLOBAL PROPERTY ${STRING_OBJECT}_OBJ_TYPE)

  if("${type}" STREQUAL SYSTEM)
    system_to_string(OBJECT ${STRING_OBJECT} STRING ${STRING_STRING})
  elseif(("${type}" STREQUAL REGION) OR ("${type}" STREQUAL GROUP))
    group_to_string(OBJECT ${STRING_OBJECT} STRING ${STRING_STRING})
  elseif("${type}" STREQUAL SECTION)
    section_to_string(SECTION ${STRING_OBJECT} STRING ${STRING_STRING})
  elseif("${type}" STREQUAL SYMBOL)
    symbol_to_string(SYMBOL ${STRING_OBJECT} STRING ${STRING_STRING})
  endif()

  set(${STRING_STRING} ${${STRING_STRING}} PARENT_SCOPE)
endfunction()

#
# String functions - end
#

create_system(OBJECT new_system NAME ZEPHYR_LINKER_v1 FORMAT ${FORMAT} ENTRY ${ENTRY})

# Sorting the memory sections in ascending order.
foreach(region ${MEMORY_REGIONS})
  if("${region}" MATCHES "^{(.*)}$")
    cmake_parse_arguments(REGION "" "NAME;START" "" ${CMAKE_MATCH_1})
    math(EXPR start_dec "${REGION_START}" OUTPUT_FORMAT DECIMAL)
    set(region_id ${start_dec}_${REGION_NAME})
    set(region_${region_id} ${region})
    string(REPLACE ";" "\;" region_${region_id} "${region_${region_id}}")
    list(APPEND region_sort ${region_id})
  endif()
endforeach()

list(SORT region_sort COMPARE NATURAL)
set(MEMORY_REGIONS_SORTED)
foreach(region_start ${region_sort})
  list(APPEND MEMORY_REGIONS_SORTED "${region_${region_start}}")
endforeach()
# sorting complete.

foreach(region ${MEMORY_REGIONS_SORTED})
  if("${region}" MATCHES "^{(.*)}$")
    create_region(OBJECT new_region ${CMAKE_MATCH_1})

    add_region(OBJECT ${new_system} REGION ${new_region})
  endif()
endforeach()

foreach(group ${GROUPS})
  if("${group}" MATCHES "^{(.*)}$")
    create_group(OBJECT new_group ${CMAKE_MATCH_1})
  endif()
endforeach()

foreach(section ${SECTIONS})
  if("${section}" MATCHES "^{(.*)}$")
    create_section(${CMAKE_MATCH_1} SYSTEM ${new_system})
  endif()
endforeach()

foreach(symbol ${SYMBOLS})
  if("${symbol}" MATCHES "^{(.*)}$")
    create_symbol(OBJECT ${new_system} ${CMAKE_MATCH_1})
  endif()
endforeach()

get_property(regions GLOBAL PROPERTY ${new_system}_REGIONS)
foreach(region ${regions})
  process_region(OBJECT ${region})
endforeach()

set(OUT)
to_string(OBJECT ${new_system} STRING OUT)

if(OUT_FILE)
  file(WRITE ${OUT_FILE} "${OUT}")
endif()
