# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------

# 源码列表调试
#[[
Parameters:
  options:
      DETAIL                        : [Optional] 是否输出详细信息
  one_value_keywords:
      NAME                          : [Required] 指定目标名称/标识名称
  multi_value_keywords:
      LIST                          : [Required] 源码列表
]]
function(PTO_Fwk_Debug_Source_List)
    cmake_parse_arguments(
            ARG
            "DETAIL"
            "NAME"
            "LIST"
            ""
            ${ARGN}
    )
    list(LENGTH ARG_LIST _Len)
    message(STATUS "${ARG_NAME}: Length: ${_Len}")
    if (ARG_DETAIL)
        foreach (_f ${ARG_LIST})
            message(STATUS "${_f}")
        endforeach ()
    endif ()
endfunction()

# 分析二进制的符号信息
#[[
Parameters:
  options:
      DF                            : [Optional] 输出已定义的关系
      IGNORE_UDF_SELF               : [Optional] 忽略自身的未定义符号(UDF: Undefined)
      IGNORE_UDF_PASSED             : [Optional] 忽略传递的未定义符号(UDF: Undefined)
  one_value_keywords:
      TARGET                        : [Required] 指定目标名称
]]
function(PTO_Fwk_AnalysisTargetSymbols)
    cmake_parse_arguments(
            ARG
            "DF;IGNORE_UDF_SELF;IGNORE_UDF_PASSED"
            "TARGET"
            ""
            ""
            ${ARGN}
    )
    if (ENABLE_COMPILE_DEPENDENCY_CHECK
            AND (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
            AND (CMAKE_C_COMPILER_ID STREQUAL "GNU"))
        set(_file $<TARGET_FILE:${ARG_TARGET}>)
        get_filename_component(_PyScript "${PTO_FWK_SRC_ROOT}/cmake/scripts/analysis_binary_symbol.py" REALPATH)
        set(_Args "-f=${_file}")
        if (ARG_DF)
            list(APPEND _Args "--print_defined_relations")
        endif ()
        if (ARG_IGNORE_UDF_SELF)
            list(APPEND _Args "--ignore_undefined_symbols_self")
        endif ()
        if (ARG_IGNORE_UDF_PASSED)
            list(APPEND _Args "--ignore_undefined_symbols_pass")
        endif ()
        set(_LdLibPathExt)
        if (ENABLE_COMPILE_DEPENDENCY_CHECK AND CMAKE_SKIP_RPATH)
            set(_LdLibPathExt ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
        endif ()
        add_custom_command(
                TARGET ${ARG_TARGET} POST_BUILD
                COMMAND cmake -E env LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${_LdLibPathExt} ${Python3_EXECUTABLE} ${_PyScript} ARGS ${_Args}
                COMMENT "Analysis symbol of ${ARG_TARGET}"
        )
    endif ()
endfunction()

# 分析二进制的头文件依赖信息
#[[
Parameters:
  one_value_keywords:
      TARGET                        : [Required] 指定目标名称
]]
function(PTO_Fwk_AnalysisTargetHeaderFiles)
    cmake_parse_arguments(
            ARG
            ""
            "TARGET"
            ""
            ""
            ${ARGN}
    )
    if (ENABLE_COMPILE_DEPENDENCY_CHECK
            AND (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
            AND (CMAKE_C_COMPILER_ID STREQUAL "GNU"))
        set(_TargetFile $<TARGET_FILE:${ARG_TARGET}>)
        get_target_property(_TargetBinaryDir ${ARG_TARGET} BINARY_DIR)
        get_filename_component(_TargetBinaryDir "${_TargetBinaryDir}" REALPATH)
        set(_TargetObjects $<TARGET_OBJECTS:${ARG_TARGET}>)
        get_filename_component(_PyScript "${PTO_FWK_SRC_ROOT}/cmake/scripts/analysis_binary_header_files.py" REALPATH)
        get_filename_component(_JsonCfg "${PTO_FWK_SRC_ROOT}/cmake/scripts/analysis_binary_header_files.json" REALPATH)
        set(_Args
                "-s=${PTO_FWK_SRC_ROOT}"
                "-b=${PTO_FWK_BIN_ROOT}"
                "-t=${_TargetFile}"
                "--target_binary_dir=${_TargetBinaryDir}"
                "-o='${_TargetObjects}'"
                "-j=${_JsonCfg}"
        )
        # 获取 gcc 默认头文件搜索路径
        execute_process(
                COMMAND ${CMAKE_C_COMPILER} --print-sysroot
                RESULT_VARIABLE _RST
                OUTPUT_VARIABLE _SUFFIX
                ERROR_QUIET
        )
        list(APPEND _Args "-f=${SYS_ROOT}/usr/include")
        list(APPEND _Args "-f=${SYS_ROOT}/usr/lib")
        # CANN
        if (BUILD_WITH_CANN)
            list(APPEND _Args "-f=${ASCEND_CANN_PACKAGE_PATH}/include")
            list(APPEND _Args "-f=${ASCEND_CANN_PACKAGE_PATH}/pkg_inc")
        endif ()
        # OpenSource
        get_target_property(json_inc json INTERFACE_INCLUDE_DIRECTORIES)
        set(OpenSourceInc ${json_inc})
        foreach (_Inc ${OpenSourceInc})
            list(APPEND _Args "-f=${_Inc}")
        endforeach ()
        list(REMOVE_DUPLICATES _Args)

        add_custom_command(
                TARGET ${ARG_TARGET} POST_BUILD
                COMMAND ${Python3_EXECUTABLE} ${_PyScript} ARGS ${_Args}
                COMMENT "Analysis Header-File of ${ARG_TARGET}"
        )
    endif ()
endfunction()

# 分析 Python3 环境信息, 主要是 pip 包信息
#[[
Parameters:
  one_value_keywords:
      OUTPUT_FILE            : [Require] 输出 cmake 文件路径
]]
function(PTO_Fwk_AnalysisPython3Environ)
    cmake_parse_arguments(
            ARG
            ""
            "OUTPUT_FILE"
            ""
            ""
            ${ARGN}
    )
    get_filename_component(_PyScript "${PTO_FWK_SRC_ROOT}/cmake/scripts/analysis_python3_environ.py" REALPATH)
    set(_Args "-o=${ARG_OUTPUT_FILE}")
    execute_process(
            COMMAND ${Python3_EXECUTABLE} ${_PyScript} ${_Args}
            RESULT_VARIABLE _Rst
    )
    if (NOT ${_Rst} EQUAL 0)
        message(FATAL_ERROR "Analysis python3 environ failed.")
    endif ()
    if (NOT EXISTS ${ARG_OUTPUT_FILE} OR IS_DIRECTORY ${ARG_OUTPUT_FILE})
        message(FATAL_ERROR "Analysis python3 environ failed, ${ARG_OUTPUT_FILE} not exist.")
    endif ()
endfunction()

function(PTO_Fwk_InstallBinaries)
    cmake_parse_arguments(
            ARG
            ""
            "EXPORT;WHL_NAME;INSTALL_BINDIR;INSTALL_LIBDIR"
            "TARGETS"
            ""
            ${ARGN}
    )
    # 设置 二进制文件 导出属性
    foreach (Target ${ARG_TARGETS})
        set_target_properties(${Target}
                PROPERTIES
                    IMPORTED_LOCATION   "$<TARGET_FILE:${Target}>"
                    OUTPUT_NAME         "${Target}"
        )
    endforeach ()
    # 安装路径设置
    if (ARG_INSTALL_BINDIR)
        set(_InstallBinDir ${ARG_INSTALL_BINDIR})
    elseif (ENABLE_FEATURE_PYTHON_FRONT_END)
        set(_InstallBinDir "${ARG_WHL_NAME}/${CMAKE_INSTALL_BINDIR}")
    else ()
        set(_InstallBinDir ${CMAKE_INSTALL_BINDIR})
    endif ()
    if (ARG_INSTALL_LIBDIR)
        set(_InstallLibDir ${ARG_INSTALL_LIBDIR})
    elseif (ENABLE_FEATURE_PYTHON_FRONT_END)
        set(_InstallLibDir "${ARG_WHL_NAME}/lib")
    else ()
        set(_InstallLibDir ${CMAKE_INSTALL_LIBDIR})
    endif ()
    # 安装 二进制文件
    install(TARGETS ${ARG_TARGETS}
            EXPORT ${ARG_EXPORT}
            RUNTIME DESTINATION ${_InstallBinDir}
            LIBRARY DESTINATION ${_InstallLibDir}
            ARCHIVE DESTINATION ${_InstallLibDir}
    )
endfunction()

function(PTO_Fwk_InstallCMakeConfig)
    cmake_parse_arguments(
            ARG
            ""
            "EXPORT;WHL_NAME;CMAKE_PARENT_DIR"
            "TARGETS"
            ""
            ${ARGN}
    )

    # 安装导出目标
    install(EXPORT ${ARG_EXPORT}
            FILE ${ARG_EXPORT}.cmake
            NAMESPACE ${PROJECT_NAME}::
            DESTINATION ${ARG_CMAKE_PARENT_DIR}/cmake/${PROJECT_NAME}
    )

    # 创建别名目标, 并创建聚合目标
    add_library(${PROJECT_NAME} INTERFACE)
    foreach (Target ${ARG_TARGETS})
        add_library(${PROJECT_NAME}::${Target} ALIAS ${Target})
        target_link_libraries(${PROJECT_NAME} INTERFACE ${PROJECT_NAME}::${Target})
    endforeach ()

    # 安装聚合目标
    install(TARGETS ${PROJECT_NAME} EXPORT ${ARG_EXPORT})

    # 生成 ConfigVersion 配置文件
    write_basic_package_version_file(
            ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
            VERSION ${PROJECT_VERSION}
            COMPATIBILITY SameMajorVersion
    )

    # 配置 Config 配置文件
    set(WHL_NAME    ${ARG_WHL_NAME})
    set(TARGETS     ${ARG_TARGETS})
    configure_package_config_file(
            ${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in
            ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
            INSTALL_DESTINATION ${ARG_CMAKE_PARENT_DIR}/cmake/${PROJECT_NAME}
            PATH_VARS
                PROJECT_NAME
                PROJECT_VERSION
                PROJECT_VERSION_MAJOR
                PROJECT_VERSION_MINOR
                PROJECT_VERSION_PATCH
                WHL_NAME
                TARGETS
            NO_CHECK_REQUIRED_COMPONENTS_MACRO
    )

    # 安装配置文件
    install(FILES
            ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
            ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
            DESTINATION ${ARG_CMAKE_PARENT_DIR}/cmake/${PROJECT_NAME}
    )
endfunction()

function(PTO_Fwk_CleanEmptyDir)
    cmake_parse_arguments(
            ARG
            ""
            "DIR"
            ""
            ""
            ${ARGN}
    )
    if (EXISTS ${ARG_DIR} AND IS_DIRECTORY ${ARG_DIR})
        file(GLOB _DirItemLst LIST_DIRECTORIES true RELATIVE "${ARG_DIR}" "${ARG_DIR}/*")
        list(LENGTH _DirItemLst _DirItemNum)
        if (_DirItemNum EQUAL 0)
            file(REMOVE_RECURSE FORCE ${ARG_DIR})
            message(STATUS "Remove empty dir: ${ARG_DIR}")
        endif()
    endif ()
endfunction()

# 生成 gcov 配置文件 (JSON 格式)
# 用于 Python 前端覆盖率生成场景,将 CMake Configure 阶段的参数持久化供外部使用
#[[
Parameters:
  one_value_keywords:
      TARGET             : [Required] 指定所依赖的目标(POST_BUILD)
  multi_value_keywords:
      FILTER_DIRECTORIES : [Optional] 覆盖率结果过滤目录
]]
function(PTO_Fwk_GenerateGCovConfigJson)
    cmake_parse_arguments(
            ARG
            ""
            "TARGET"
            "FILTER_DIRECTORIES"
            ""
            ${ARGN}
    )
    if (ENABLE_FEATURE_PYTHON_FRONT_END AND ENABLE_GCOV)
        # 获取 gcc 默认头文件搜索路径
        execute_process(
                COMMAND ${CMAKE_C_COMPILER} --print-sysroot
                RESULT_VARIABLE _SYSROOT_RST
                OUTPUT_VARIABLE _SYSROOT_SUFFIX
                ERROR_QUIET
        )
        if (_SYSROOT_RST)
            get_filename_component(SYS_ROOT "/usr/include" REALPATH)
        else ()
            get_filename_component(SYS_ROOT "${_SYSROOT_SUFFIX}/usr/include" REALPATH)
        endif ()

        set(Filter_Dirs
                ${PTO_FWK_SRC_ROOT}/framework/tests
                $<TARGET_PROPERTY:json,INTERFACE_INCLUDE_DIRECTORIES>
                ${SYS_ROOT}
                ${Python3_INCLUDE_DIRS}
                ${pybind11_INCLUDE_DIR}
                ${ARG_FILTER_DIRECTORIES}
        )
        if (ENABLE_TORCH_VERIFIER)
            list(APPEND Filter_Dirs ${PY3_MOD_TORCH_ROOT_PATH}/include)
        endif ()
        if (BUILD_WITH_CANN)
            list(APPEND Filter_Dirs ${ASCEND_CANN_PACKAGE_PATH}/include)
            list(APPEND Filter_Dirs ${ASCEND_CANN_PACKAGE_PATH}/pkg_inc)
        endif ()

        # 参数组织
        set(_Args "-d=${PTO_FWK_BIN_ROOT}")
        foreach (_dir ${Filter_Dirs})
            list(APPEND _Args "-f=${_dir}")
        endforeach ()
        list(REMOVE_DUPLICATES _Args)

        # 脚本调用
        get_filename_component(GenCoverageCfgPy ${PTO_FWK_SRC_ROOT}/cmake/scripts/gen_coverage_config.py REALPATH)
        add_custom_command(
                TARGET ${ARG_TARGET} POST_BUILD
                COMMAND ${Python3_EXECUTABLE} ${GenCoverageCfgPy} ARGS ${_Args}
                VERBATIM
                COMMENT "Generate coverage config for ${ARG_TARGET}"
        )
    endif()
endfunction()