include_guard()

include(colorful-message)

option(AUTODETECT_ALTERNATIVE_LINKERS "Enable GNU gold/LLD linker detection" ON)
set(LLD_LINKER_ID "LLD")
set(GNU_GOLD_LINKER_ID "GNU gold")
if(NOT PREFERED_LINKER)
  set(PREFERED_LINKER
      "None"
      CACHE
        STRING
        "Prefered linker during auto-detection, choose from ${LLD_LINKER_ID}, ${GNU_GOLD_LINKER_ID} and None"
        FORCE)
  set_property(CACHE PREFERED_LINKER PROPERTY STRINGS ${LLD_LINKER_ID} ${GNU_GOLD_LINKER_ID} "None")
endif()

set(LLD_LINKER_FLAG "-fuse-ld=lld")
set(GOLD_LINKER_FLAG "-fuse-ld=gold")

execute_process(
  COMMAND ${CMAKE_CXX_COMPILER} ${LLD_LINKER_FLAG} -Wl,-v
  ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
  OUTPUT_VARIABLE TRY_DETECT_LLD_LINKER)

execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${GOLD_LINKER_FLAG} -Wl,-v ERROR_QUIET
                        OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE TRY_DETECT_GNU_GOLD_LINKER)

set(USER_PROVIDED_LINKER_FLAG "-fuse-ld=")
if((${CMAKE_EXE_LINKER_FLAGS} MATCHES ${USER_PROVIDED_LINKER_FLAG})
   OR (${CMAKE_SHARED_LINKER_FLAGS} MATCHES ${USER_PROVIDED_LINKER_FLAG})
   OR (${CMAKE_CXX_FLAGS} MATCHES ${USER_PROVIDED_LINKER_FLAG}))
  set(USER_PROVIDED_LINKER TRUE)
endif()

if(NOT USER_PROVIDED_LINKER AND AUTODETECT_ALTERNATIVE_LINKERS)
  if(${TRY_DETECT_LLD_LINKER} MATCHES ${LLD_LINKER_ID})
    set(DETECTED_LLD_LINKER TRUE)
  endif()
  if(${TRY_DETECT_GNU_GOLD_LINKER} MATCHES ${GNU_GOLD_LINKER_ID})
    set(DETECTED_GNU_GOLD_LINKER TRUE)
  endif()
  if(DETECTED_LLD_LINKER AND DETECTED_GNU_GOLD_LINKER)
    set(DETECTED_BOTH_LINKERS TRUE)
  endif()
  if(${PREFERED_LINKER} MATCHES ${LLD_LINKER_ID})
    set(LLD_IS_PREFERED TRUE)
  endif()
  if(${PREFERED_LINKER} MATCHES ${GNU_GOLD_LINKER_ID})
    set(GNU_GOLD_IS_PREFERED TRUE)
  endif()
  if(${PREFERED_LINKER} MATCHES "None")
    set(PREFERED_LINKED_NOT_SET TRUE)
  endif()
  if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
    set(CXX_SET_TO_CLANG TRUE)
  endif()
  if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
    set(CXX_SET_TO_GNU TRUE)
  endif()

  if((DETECTED_BOTH_LINKERS AND LLD_IS_PREFERED)
     OR (CXX_SET_TO_CLANG
         AND DETECTED_BOTH_LINKERS
         AND PREFERED_LINKED_NOT_SET)
     OR (DETECTED_LLD_LINKER AND NOT DETECTED_GNU_GOLD_LINKER))
    set(PROJECT_LINKER ${TRY_DETECT_LLD_LINKER})
    add_link_options(${LLD_LINKER_FLAG})
  endif()

  if((DETECTED_BOTH_LINKERS AND GNU_GOLD_IS_PREFERED)
     OR (CXX_SET_TO_GNU
         AND DETECTED_BOTH_LINKERS
         AND PREFERED_LINKED_NOT_SET)
     OR (DETECTED_GNU_GOLD_LINKER AND NOT DETECTED_LLD_LINKER))
    set(PROJECT_LINKER ${TRY_DETECT_GNU_GOLD_LINKER})
    add_link_options(${GOLD_LINKER_FLAG})
  endif()

  if(PROJECT_LINKER)
    cmessage(STATUS "Using auto-detected linker: ${PROJECT_LINKER}")
  else()
    cmessage(STATUS "GNU gold/LLD not detected. Using default OS linker")
  endif()
else()
  cmessage(STATUS "Using linker provided by user")
endif()
