# SPDX-License-Identifier: Apache-2.0

find_program(CMAKE_LINKER ${CROSS_COMPILE}lldac PATH ${TOOLCHAIN_HOME} NO_DEFAULT_PATH)

# the prefix to transfer linker options from compiler
set_ifndef(LINKERFLAGPREFIX -Wl,)

# Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen}
# NOTE: ${linker_script_gen} will be produced at build-time; not at configure-time
macro(configure_linker_script linker_script_gen linker_pass_define)
  set(extra_dependencies ${ARGN})

  # Different generators deal with depfiles differently.
  if(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
    # Note that the IMPLICIT_DEPENDS option is currently supported only
    # for Makefile generators and will be ignored by other generators.
    set(linker_script_dep IMPLICIT_DEPENDS C ${LINKER_SCRIPT})
  elseif(CMAKE_GENERATOR STREQUAL "Ninja")
    # Using DEPFILE with other generators than Ninja is an error.
    set(linker_script_dep DEPFILE ${PROJECT_BINARY_DIR}/${linker_script_gen}.dep)
  else()
    # TODO: How would the linker script dependencies work for non-linker
    # script generators.
    message(STATUS "Warning; this generator is not well supported. The
                    Linker script may not be regenerated when it should.")
    set(linker_script_dep "")
  endif()

  zephyr_get_include_directories_for_lang(C current_includes)
  get_filename_component(base_name ${CMAKE_CURRENT_BINARY_DIR} NAME)
  get_property(current_defines GLOBAL PROPERTY PROPERTY_LINKER_SCRIPT_DEFINES)

# the command to generate linker file from template
  add_custom_command(
    OUTPUT ${linker_script_gen}
    DEPENDS
    ${LINKER_SCRIPT}
    ${AUTOCONF_H}
    ${extra_dependencies}
    # NB: 'linker_script_dep' will use a keyword that ends 'DEPENDS'
    ${linker_script_dep}
    COMMAND ${CMAKE_C_COMPILER}
    -x c
    ${NOSYSDEF_CFLAG}
    -Hnocopyr
    -MD -MF ${linker_script_gen}.dep -MT ${base_name}/${linker_script_gen}
    -D_LINKER
    -D_ASMLANGUAGE
    -imacros ${AUTOCONF_H}
    ${current_includes}
    ${current_defines}
    ${linker_pass_define}
    ${LINKER_SCRIPT}
    -E
    -o ${linker_script_gen}
    VERBATIM
    WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
    COMMAND_EXPAND_LISTS
)
endmacro()

# Force symbols to be entered in the output file as undefined symbols
function(toolchain_ld_force_undefined_symbols)
  foreach(symbol ${ARGN})
    zephyr_link_libraries(${LINKERFLAGPREFIX}-u${symbol})
  endforeach()
endfunction()

# Link a target to given libraries with toolchain-specific argument order
#
# Usage:
#   toolchain_ld_link_elf(
#     TARGET_ELF             <target_elf>
#     OUTPUT_MAP             <output_map_file_of_target>
#     LIBRARIES_PRE_SCRIPT   [libraries_pre_script]
#     LINKER_SCRIPT          <linker_script>
#     LIBRARIES_POST_SCRIPT  [libraries_post_script]
#     DEPENDENCIES           [dependencies]
#   )
function(toolchain_ld_link_elf)
  cmake_parse_arguments(
    TOOLCHAIN_LD_LINK_ELF                                     # prefix of output variables
    ""                                                        # list of names of the boolean arguments
    "TARGET_ELF;OUTPUT_MAP;LINKER_SCRIPT"                     # list of names of scalar arguments
    "LIBRARIES_PRE_SCRIPT;LIBRARIES_POST_SCRIPT;DEPENDENCIES" # list of names of list arguments
    ${ARGN}                                                   # input args to parse
  )

  target_link_libraries(
    ${TOOLCHAIN_LD_LINK_ELF_TARGET_ELF}
    ${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_PRE_SCRIPT}
    ${LINKERFLAGPREFIX}-T${TOOLCHAIN_LD_LINK_ELF_LINKER_SCRIPT}
    ${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_POST_SCRIPT}
    ${LINKERFLAGPREFIX}--gc-sections
    ${LINKERFLAGPREFIX}--entry=__start
    ${LINKERFLAGPREFIX}--Map=${TOOLCHAIN_LD_LINK_ELF_OUTPUT_MAP}
    ${LINKERFLAGPREFIX}--whole-archive
    ${ZEPHYR_LIBS_PROPERTY}
    ${LINKERFLAGPREFIX}--no-whole-archive
    kernel
    $<TARGET_OBJECTS:${OFFSETS_LIB}>
    ${LIB_INCLUDE_DIR}
    -L${PROJECT_BINARY_DIR}
    ${TOOLCHAIN_LIBS}

    ${TOOLCHAIN_LD_LINK_ELF_DEPENDENCIES}
  )
endfunction(toolchain_ld_link_elf)

# linker options of temporary linkage for code generation
macro(toolchain_ld_baremetal)
  zephyr_ld_options(
    -Hlld
    -Hnosdata
    -Hnocrt
    -Xtimer0 # to suppress the warning message
    -Hnoxcheck_obj
    -Hnocplus
    -Hheap=0
    -Hnoivt
  )

  # Funny thing is if this is set to =error, some architectures will
  # skip this flag even though the compiler flag check passes
  # (e.g. ARC and Xtensa). So warning should be the default for now.
  #
  # Skip this for native application as Zephyr only provides
  # additions to the host toolchain linker script. The relocation
  # sections (.rel*) requires us to override those provided
  # by host toolchain. As we can't account for all possible
  # combination of compiler and linker on all machines used
  # for development, it is better to turn this off.
  #
  # CONFIG_LINKER_ORPHAN_SECTION_PLACE is to place the orphan sections
  # without any warnings or errors, which is the default behavior.
  # So there is no need to explicitly set a linker flag.
  if(CONFIG_LINKER_ORPHAN_SECTION_WARN)
    message(WARNING "MWDT toolchain does not support
           CONFIG_LINKER_ORPHAN_SECTION_WARN")
  elseif(CONFIG_LINKER_ORPHAN_SECTION_ERROR)
    zephyr_ld_options(
      ${LINKERFLAGPREFIX}--orphan-handling=error)
  endif()
endmacro()

# base linker options
macro(toolchain_ld_base)
  if(NOT PROPERTY_LINKER_SCRIPT_DEFINES)
    set_property(GLOBAL PROPERTY PROPERTY_LINKER_SCRIPT_DEFINES -D__MWDT_LINKER_CMD__)
  endif()

  # Sort the common symbols and each input section by alignment
  # in descending order to minimize padding between these symbols.
  zephyr_ld_option_ifdef(
    CONFIG_LINKER_SORT_BY_ALIGNMENT
    ${LINKERFLAGPREFIX}--sort-section=alignment
  )
endmacro()

# generate linker script snippts from configure files
macro(toolchain_ld_configure_files)
  configure_file(
       $ENV{ZEPHYR_BASE}/include/arch/common/app_data_alignment.ld
       ${PROJECT_BINARY_DIR}/include/generated/app_data_alignment.ld)

  configure_file(
       $ENV{ZEPHYR_BASE}/include/linker/app_smem.ld
       ${PROJECT_BINARY_DIR}/include/generated/app_smem.ld)

  configure_file(
       $ENV{ZEPHYR_BASE}/include/linker/app_smem_aligned.ld
       ${PROJECT_BINARY_DIR}/include/generated/app_smem_aligned.ld)

  configure_file(
       $ENV{ZEPHYR_BASE}/include/linker/app_smem_unaligned.ld
       ${PROJECT_BINARY_DIR}/include/generated/app_smem_unaligned.ld)
endmacro()

# link C++ libraries
macro(toolchain_ld_cpp)
  zephyr_link_libraries(
    -Hcppmw -Hcplus
  )
endmacro()

# use linker for relocation
macro(toolchain_ld_relocation)
  set(MEM_RELOCATION_LD   "${PROJECT_BINARY_DIR}/include/generated/linker_relocate.ld")
  set(MEM_RELOCATION_SRAM_DATA_LD
       "${PROJECT_BINARY_DIR}/include/generated/linker_sram_data_relocate.ld")
  set(MEM_RELOCATION_SRAM_BSS_LD
       "${PROJECT_BINARY_DIR}/include/generated/linker_sram_bss_relocate.ld")
  set(MEM_RELOCATION_CODE "${PROJECT_BINARY_DIR}/code_relocation.c")

  add_custom_command(
    OUTPUT ${MEM_RELOCATION_CODE} ${MEM_RELOCATION_LD}
    COMMAND
    ${PYTHON_EXECUTABLE}
    ${ZEPHYR_BASE}/scripts/gen_relocate_app.py
    $<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
    -d ${APPLICATION_BINARY_DIR}
    -i \"$<TARGET_PROPERTY:code_data_relocation_target,COMPILE_DEFINITIONS>\"
    -o ${MEM_RELOCATION_LD}
    -s ${MEM_RELOCATION_SRAM_DATA_LD}
    -b ${MEM_RELOCATION_SRAM_BSS_LD}
    -c ${MEM_RELOCATION_CODE}
    DEPENDS app kernel ${ZEPHYR_LIBS_PROPERTY}
    )

  add_library(code_relocation_source_lib  STATIC ${MEM_RELOCATION_CODE})
  target_link_libraries(code_relocation_source_lib zephyr_interface)
endmacro()
