if ((NOT WIN32) OR MINGW)
    set(split_libs_default ON)
else ()
    set(split_libs_default OFF)
endif ()
option(GDAL_SPLIT_EXPORTED_LIBS "Split library path and name on export" ${split_libs_default})


# Join the list items and add double quotes around items which contain whitespace
function(gdal_join_and_quote _var)
    set(string "")
    foreach(item IN LISTS ${_var})
        if("${item}" MATCHES " ")
            set(item "\"${item}\"")
        endif()
        if(NOT "${string}" STREQUAL "")
            string(APPEND string " ")
        endif()
        string(APPEND string "${item}")
    endforeach()
    set(${_var} "${string}" PARENT_SCOPE)
endfunction()

# Return a flat list of libs including target linking requirements,
# prepended to _result input. Targets appear only once, as late as possible.
# Targets which are already in _result are not resolved/added again.
function(gdal_flatten_link_libraries _result _is_debug_build)
    set(_libs "${${_result}}")
    gdal_process_link_keywords(working_list "${_is_debug_build}" ${ARGN})
    while(working_list)
        list(POP_BACK working_list _lib)
        if(NOT TARGET "${_lib}")
            list(PREPEND _libs "${_lib}")
        elseif(_lib IN_LIST _libs)
            # already resolved, do not repeat
            continue()
        else()
            # new target
            get_target_property(_link_libraries ${_lib} INTERFACE_LINK_LIBRARIES)
            get_target_property(_type ${_lib} TYPE)
            if(_link_libraries AND NOT TYPE STREQUAL "SHARED_LIBRARY")
                gdal_flatten_link_libraries(_libs "${_is_debug_build}" ${_link_libraries})
            endif()
            list(PREPEND _libs "${_lib}")
        endif()
    endwhile()
    set(${_result} "${_libs}" PARENT_SCOPE)
endfunction()

# Process link keywords and generator expressions for the purpose of exporting linker flags.
function(gdal_process_link_keywords _result _is_debug_build)
    set(_libs "")
    while(ARGN)
        list(POP_FRONT ARGN _lib)
        if(_lib STREQUAL "debug")
            if(NOT _is_debug_build)
                list(POP_FRONT ARGN)
            endif()
            continue()
        elseif(_lib STREQUAL "optimized")
            if(_is_debug_build)
                list(POP_FRONT ARGN)
            endif()
            continue()
        elseif(_lib STREQUAL "general")
            continue()
        endif()
        gdal_resolve_link_genex(_lib "${_lib}")
        if(_lib)
            list(APPEND _libs "${_lib}")
        endif()
    endwhile()
    set(${_result} "${_libs}" PARENT_SCOPE)
endfunction()

# Resolve all generator expressions for the purpose of exporting linker flags.
function(gdal_resolve_link_genex _result _input)
    while(_input MATCHES "^(.*)\\\$<([^:>]*):*([^>]*)>")
        set(_match "${CMAKE_MATCH_0}")
        set(_first "${CMAKE_MATCH_1}")
        gdal_evaluate_link_genex(_second "${CMAKE_MATCH_2}" "${CMAKE_MATCH_3}")
        string(REPLACE "${_match}" "${_first}${_second}" _input "${_input}")
    endwhile()
    set("${_result}" "${_input}" PARENT_SCOPE)
endfunction()

# Resolve a single generator expressions for the purpose of exporting linker flags.
function(gdal_evaluate_link_genex _result _keyword _params)
    if(_keyword STREQUAL "1" OR _keyword STREQUAL "LINK_ONLY")
        set(value "${_params}")
    elseif(_keyword STREQUAL "CONFIG")
        # Should use the target property, not the variable, but we don't track the target ATM
        string(TOUPPER "${CMAKE_BUILD_TYPE}" current_config)
        if(DEFINED "CMAKE_MAP_IMPORTED_CONFIG_${current_config}")
            string(TOUPPER "${CMAKE_MAP_IMPORTED_CONFIG_${current_config}}" configs)
        else()
            set(configs "${current_config}")
        endif()
        if(_params IN_LIST configs)
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword MATCHES "COMPILER_ID\$")
        string(REPLACE "," ";" compiler_ids "${_params}")
        if("${CMAKE_${_keyword}}" IN_LIST compiler_ids)
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword STREQUAL "PLATFORM_ID")
        string(REPLACE "," ";" platform_ids "${_params}")
        if("${CMAKE_CXX_PLATFORM_ID}" IN_LIST "${platform_ids}")
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword STREQUAL "NOT")
        if(_params STREQUAL "0" OR _params STREQUAL "")
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword STREQUAL "AND")
        string(REPLACE "," ";" params_list "${_params}")
        if("0" IN_LIST params_list)
            set(value 0)
        else()
            set(value 1)
        endif()
    elseif(_keyword STREQUAL "OR")
        string(REPLACE "," ";" params_list "${_params}")
        if("1" IN_LIST params_list)
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword STREQUAL "BOOL")
        if("${_params}")
            set(value 1)
        else()
            set(value 0)
        endif()
    elseif(_keyword STREQUAL "0")
        set(value "")
    else()
        if(NOT _params STREQUAL "")
            string(APPEND _keyword ":${_params}")
        endif()
        message(WARNING "Dropping unsupported generator expression: '\$<${_keyword}>'")
        set(value "")
    endif()
    set(${_result} "${value}" PARENT_SCOPE)
endfunction()


# Get a target property with respect to the current build type
function(gdal_get_target_property_for_config _result _target _property)
    string(TOUPPER "${CMAKE_BUILD_TYPE}" current_config)
    get_target_property(mapped_configs ${_target} MAP_IMPORTED_CONFIG_${current_config})
    if (NOT mapped_configs AND NOT mapped_configs STREQUAL "" AND NOT current_config STREQUAL "")
        set(mapped_configs "${current_config}")
    endif ()
    string(TOUPPER "${mapped_configs}" mapped_configs)
    set(all_properties "")
    foreach (mapped_config IN LISTS mapped_configs)
        list(APPEND all_properties "${_property}_${mapped_config}")
    endforeach()
    list(APPEND all_properties "${_property}")
    get_target_property(imported_configs ${_target} IMPORTED_CONFIGURATIONS)
    foreach (imported_config IN LISTS imported_configs)
        list(APPEND all_properties "${_property}_${imported_config}")
    endforeach()
    foreach(candidate_property IN LISTS all_properties)
        get_target_property(value ${_target} ${candidate_property})
        if(value)
            break()
        endif()
    endforeach ()
    set(${_result} "${value}" PARENT_SCOPE)
endfunction()

# Guess linker parameters (-L/osgeo -lproj) from file name (/osgeo/libproj.dll.a).
function(gdal_split_library_to_lflags _lib_flag _path_flag _input)
    get_filename_component(_lib_name "${_input}" NAME)
    foreach(_item IN ITEMS SHARED IMPORT STATIC)
        set(_suffix "${CMAKE_${_item}_LIBRARY_SUFFIX}")
        if(NOT _suffix)
            continue()
        endif()
        string(FIND "${_lib_name}" "${_suffix}" _pos REVERSE)
        if(_pos EQUAL "-1")
            continue()
        endif()
        get_filename_component(_lib_dir "${_input}" DIRECTORY)
        string(SUBSTRING "${_lib_name}" 0 "${_pos}" _name_we)
        if(NOT "${_name_we}${_suffix}" STREQUAL _lib_name)
            if(NOT _suffix STREQUAL ".so" OR NOT EXISTS "${_lib_dir}/${_name_we}${_suffix}")
                continue()
            endif()
        endif()
        set(_prefix "${CMAKE_${_item}_LIBRARY_PREFIX}")
        if(_prefix)
            string(FIND "${_name_we}" "${_prefix}" _pos)
        else()
            set(_pos 0)
        endif()
        if(_pos STREQUAL "0")
            # Match
            string(LENGTH "${_prefix}" _pos)
            string(SUBSTRING "${_name_we}" "${_pos}" "-1" _lib_name)
            set(${_lib_flag} "${CMAKE_LINK_LIBRARY_FLAG}${_lib_name}" PARENT_SCOPE)
            if(_lib_dir AND NOT _lib_dir IN_LIST CMAKE_C_IMPLICIT_LINK_DIRECTORIES)
                set(${_path_flag} "${CMAKE_LIBRARY_PATH_FLAG}${_lib_dir}" PARENT_SCOPE)
            else()
                set(${_path_flag} "" PARENT_SCOPE)
            endif()
            return()
        endif()
    endforeach()
    # Fallback
    set(${_lib_flag} "${_input}")
    set(${_path_flag} "" PARENT_SCOPE)
endfunction()

# Get linker flags for cmake link libraries, with targets and generator
# expressions resolved for the current build type.
function(gdal_get_lflags _result)
    string(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" is_debug_build)
    if(CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE IN_LIST DEBUG_CONFIGURATIONS)
        set(is_debug_build TRUE)
    endif()

    set(_libs_in "")
    gdal_flatten_link_libraries(_libs_in "${is_debug_build}" ${ARGN})

    set(_libs_out "")
    foreach(_lib IN LISTS _libs_in)
        if(TARGET "${_lib}")
            get_property(_type TARGET ${_lib} PROPERTY TYPE)
            if(_type STREQUAL "INTERFACE_LIBRARY")
                continue()
            endif()
            set(_location "")
            if(NOT _type STREQUAL "STATIC_LIBRARY")
                gdal_get_target_property_for_config(_location "${_lib}" "IMPORTED_IMPLIB")
            endif()
            if(NOT _location)
                gdal_get_target_property_for_config(_location "${_lib}" "IMPORTED_LOCATION")
            endif()
            if(_location)
                set(_lib "${_location}")
            endif()
        endif()
        set(_other "")
        if(CMAKE_LIBRARY_PATH_FLAG STREQUAL "-L" AND _lib MATCHES "^-L")
            set(_other "${_lib}")
            set(_lib "")
        elseif(_lib STREQUAL "-pthread")
            # use _lib
        elseif(CMAKE_LINK_LIBRARY_FLAG STREQUAL "-l" AND _lib MATCHES "^-l")
            # use _lib
        elseif(EXISTS "${_lib}")
            if(GDAL_SPLIT_EXPORTED_LIBS)
                gdal_split_library_to_lflags(_lib _other "${_lib}")
            endif()
        else()
            set(_lib "${CMAKE_LINK_LIBRARY_FLAG}${_lib}")
        endif()
        if(_other AND NOT _other IN_LIST _libs_out)
            list(APPEND _libs_out "${_other}")
        endif()
        if(_lib)
            list(REMOVE_ITEM _libs_out "${_lib}")
            list(APPEND _libs_out "${_lib}")
        endif()
    endforeach()
    set(${_result} "${_libs_out}" PARENT_SCOPE)
endfunction()

# Generate gdal-config utility command and pkg-config module gdal.pc
function(gdal_generate_config)
    set(one_value_keywords "TARGET;GLOBAL_PROPERTY;GDAL_CONFIG;PKG_CONFIG")
    cmake_parse_arguments(PARSE_ARGV 0 arg "" "${one_value_keywords}" "")

    if (ENABLE_GNM)
        set(CONFIG_GNM_ENABLED "yes")
    else ()
        set(CONFIG_GNM_ENABLED "no")
    endif ()
    get_property(gdal_formats GLOBAL PROPERTY GDAL_FORMATS)
    get_property(ogr_formats GLOBAL PROPERTY OGR_FORMATS)
    string(REPLACE ";" " " CONFIG_FORMATS "${gdal_formats} ${ogr_formats}")

    if(NOT DEFINED CMAKE_INSTALL_PREFIX)
        set(CONFIG_PREFIX "/usr/local") # default
    else()
        set(CONFIG_PREFIX "${CMAKE_INSTALL_PREFIX}")
    endif()
    set(CONFIG_DATA "${CONFIG_PREFIX}/${GDAL_RESOURCE_PATH}")
    set(CONFIG_CFLAGS "-I${CMAKE_INSTALL_FULL_INCLUDEDIR}")
    gdal_join_and_quote(CONFIG_DATA)
    gdal_join_and_quote(CONFIG_CFLAGS)

    get_property(target_lib_name TARGET "${arg_TARGET}" PROPERTY OUTPUT_NAME)
    set(CONFIG_LIBS "${CMAKE_LINK_LIBRARY_FLAG}${target_lib_name}")
    if(NOT CONFIG_PREFIX IN_LIST CMAKE_C_IMPLICIT_LINK_LIBRARIES)
        list(INSERT CONFIG_LIBS 0 "${CMAKE_LIBRARY_PATH_FLAG}${CMAKE_INSTALL_FULL_LIBDIR}")
    endif()
    gdal_join_and_quote(CONFIG_LIBS)

    # For gdal.pc libdir variable. Not strictly needed, but quite standard
    # to have one in a .pc
    if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
        if( "${CMAKE_INSTALL_LIBDIR}" MATCHES "^${CONFIG_PREFIX}.*")
            file(RELATIVE_PATH _rel_path "${CONFIG_PREFIX}" "${CMAKE_INSTALL_LIBDIR}")
            set(CONFIG_LIBDIR "\${exec_prefix}/${_rel_path}")
        else()
            set(CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
        endif()
    else()
        set(CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
    endif()
    gdal_join_and_quote(CONFIG_LIBDIR)

    # For gdal.pc includedir variable. Not strictly needed, but quite standard
    # to have one in a .pc
    if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
        if( "${CMAKE_INSTALL_INCLUDEDIR}" MATCHES "^${CONFIG_PREFIX}.*")
            file(RELATIVE_PATH _rel_path "${CONFIG_PREFIX}" "${CMAKE_INSTALL_INCLUDEDIR}")
            set(CONFIG_INCLUDEDIR "\${exec_prefix}/${_rel_path}")
        else()
            set(CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
        endif()
    else()
        set(CONFIG_INCLUDEDIR "\${exec_prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
    endif()
    gdal_join_and_quote(CONFIG_INCLUDEDIR)

    get_property(libs GLOBAL PROPERTY "${arg_GLOBAL_PROPERTY}")
    if(NOT MSVC AND CMAKE_THREAD_LIBS_INIT)
        list(APPEND libs ${CMAKE_THREAD_LIBS_INIT})
    endif()
    list(APPEND libs ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES})
    if(CMAKE_C_IMPLICIT_LINK_LIBRARIES)
        list(REMOVE_ITEM libs ${CMAKE_C_IMPLICIT_LINK_LIBRARIES})
    endif()
    gdal_get_lflags(CONFIG_DEP_LIBS ${libs})
    gdal_join_and_quote(CONFIG_DEP_LIBS)
    if(NOT BUILD_SHARED_LIBS)
        # Make `--libs` simply work, even for static builds.
        string(APPEND CONFIG_LIBS " ${CONFIG_DEP_LIBS}")
        set(CONFIG_DEP_LIBS "")
    endif()

    gdal_join_and_quote(CONFIG_PREFIX)

    # For gdal-config --plugindir
    set(CONFIG_PLUGINDIR "${INSTALL_PLUGIN_FULL_DIR}")
    gdal_join_and_quote(CONFIG_PLUGINDIR)

    # Create apps/gdal-config with execution rights from gdal-config.in
    get_filename_component(GDAL_CONFIG_DIR "${arg_GDAL_CONFIG}" DIRECTORY)
    file(MAKE_DIRECTORY "${GDAL_CONFIG_DIR}.tmp")
    configure_file("${GDAL_CMAKE_TEMPLATE_PATH}/gdal-config.in" "${GDAL_CONFIG_DIR}.tmp/gdal-config" @ONLY)
    file(COPY "${GDAL_CONFIG_DIR}.tmp/gdal-config"
         DESTINATION "${GDAL_CONFIG_DIR}"
         FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

    configure_file("${GDAL_CMAKE_TEMPLATE_PATH}/gdal.pc.in" "${arg_PKG_CONFIG}" @ONLY)
endfunction()
