# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 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.
# -----------------------------------------------------------------------------------------------------------
# 初始化superbuild工程
macro(init_cann_superbuild_project)
cmake_parse_arguments(CANN "" "PRODUCT_SIDE" "" ${ARGN})
if(LAUNCH_COMPILE_TOOL)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${LAUNCH_COMPILE_TOOL}")
endif()
if(LAUNCH_LINK_TOOL)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${LAUNCH_LINK_TOOL}")
endif()
if(CANN_PRODUCT_SIDE)
set(PRODUCT_SIDE "${CANN_PRODUCT_SIDE}")
endif()
if(PRODUCT_SIDE STREQUAL "device")
get_filename_component(CANN_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
else()
get_filename_component(CANN_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
endif()
include(${CANN_CMAKE_DIR}/function/prepare.cmake)
init_cann_project(${ARGN})
get_filename_component(CANN_TOP_DIR "${CANN_CMAKE_DIR}/.." REALPATH)
load_superbuild_config()
calc_cann_binary_components()
endmacro()
# 计算二进制组件
function(calc_cann_binary_components)
set(CANN_BINARY_COMPONENTS)
set(CANN_BINARY_COMPONENTS_ALL FALSE)
foreach(PKG IN LISTS CANN_BINARY_PACKAGES)
string(TOLOWER "${PKG}" PKG_LOWER)
if(PKG_LOWER STREQUAL "all")
set(CANN_BINARY_COMPONENTS_ALL TRUE)
else()
list(APPEND CANN_BINARY_COMPONENTS ${CANN_PACKAGE_COMPONENTS_${PKG}})
endif()
endforeach()
set(CANN_BINARY_COMPONENTS "${CANN_BINARY_COMPONENTS}" PARENT_SCOPE)
set(CANN_BINARY_COMPONENTS_ALL "${CANN_BINARY_COMPONENTS_ALL}" PARENT_SCOPE)
endfunction()
# 加载配置
macro(load_superbuild_config)
include(${CANN_CMAKE_DIR}/superbuild/config.cmake)
if(CANN_SUPERBUILD_CONFIG AND EXISTS ${CANN_SUPERBUILD_CONFIG})
include(${CANN_SUPERBUILD_CONFIG})
endif()
endmacro()
# 获取包对应源码目录
function(get_package_dir_by_package outvar package)
if(ARGN)
list(GET ARGN 0 subdirs)
else()
set(subdirs)
endif()
if(CANN_PACKAGE_DIR_${package})
set(pkg_dir "${CANN_PACKAGE_DIR_${package}}")
else()
set(pkg_dir "${package}")
endif()
if(subdirs)
set(pkg_dir "${pkg_dir}/${subdirs}")
endif()
set(${outvar} "${pkg_dir}" PARENT_SCOPE)
endfunction()
# 获取包对应源码目录
function(get_package_dirs outvar pkgs)
set(pkg_dirs)
foreach(pkg IN LISTS pkgs)
get_package_dir_by_package(pkg_dir "${pkg}" ${ARGN})
list(APPEND pkg_dirs "${pkg_dir}")
endforeach()
list(REMOVE_DUPLICATES pkg_dirs)
set(${outvar} "${pkg_dirs}" PARENT_SCOPE)
endfunction()
# 过滤目标列表
function(filter_targets outvar)
unset(EXISTS_TARGETS)
unset(NOT_EXISTS_TARGETS)
foreach(TGT IN LISTS ARGN)
# 目标名以maybe::开头,表明该目标可能存在, 不参与目标存在性强校验
string(REGEX REPLACE "^maybe::" "" NEW_TGT "${TGT}")
# 如果替换后的串与原串不同, 说明原串以maybe::开头
if(NOT(TGT STREQUAL NEW_TGT))
if(TARGET ${NEW_TGT})
# 存在的目标, 将去除maybe::的目标名加入存在列表
list(APPEND EXISTS_TARGETS ${NEW_TGT})
else()
# 不存在的目标, 将包含maybe::的目标名加入不存在的列表
list(APPEND NOT_EXISTS_TARGETS ${TGT})
endif()
else()
if(TARGET ${TGT})
list(APPEND EXISTS_TARGETS ${TGT})
else()
list(APPEND NOT_EXISTS_TARGETS ${TGT})
endif()
endif()
endforeach()
set(${outvar} ${EXISTS_TARGETS} "__SEP__" ${NOT_EXISTS_TARGETS} PARENT_SCOPE)
endfunction(filter_targets)
# 获取存在的目标
function(get_exists_targets outvar)
unset(RESULT)
foreach(TGT IN LISTS ARGN)
if(TGT STREQUAL "__SEP__")
break()
endif()
list(APPEND RESULT ${TGT})
endforeach()
set(${outvar} "${RESULT}" PARENT_SCOPE)
endfunction()
# 获取不存在的目标
function(get_not_exists_targets outvar)
unset(RESULT)
set(NEED_APPEND FALSE)
foreach(TGT IN LISTS ARGN)
if(TGT STREQUAL "__SEP__")
set(NEED_APPEND TRUE)
continue()
endif()
if(NOT NEED_APPEND)
continue()
endif()
# maybe目标不加入不存在列表
if(NOT(TGT MATCHES "^maybe::"))
list(APPEND RESULT ${TGT})
endif()
endforeach()
set(${outvar} "${RESULT}" PARENT_SCOPE)
endfunction()
function(get_targets_in_directory out_var dirpath)
get_property(targets DIRECTORY "${dirpath}" PROPERTY "BUILDSYSTEM_TARGETS")
get_property(subdirs DIRECTORY "${dirpath}" PROPERTY "SUBDIRECTORIES")
foreach(subdir IN LISTS subdirs)
get_property(exclude_from_all DIRECTORY "${subdir}" PROPERTY "EXCLUDE_FROM_ALL")
if(exclude_from_all)
continue()
endif()
get_targets_in_directory(sub_targets "${subdir}")
list(APPEND targets ${sub_targets})
endforeach()
set("${out_var}" "${targets}" PARENT_SCOPE)
endfunction()
function(filter_build_targets out_var targets)
set(build_targets)
foreach(target IN LISTS targets)
get_property(type TARGET "${target}" PROPERTY "TYPE")
if(type STREQUAL "INTERFACE_LIBRARY")
continue()
endif()
get_property(exclude_from_all TARGET "${target}" PROPERTY "EXCLUDE_FROM_ALL")
if(exclude_from_all)
continue()
endif()
list(APPEND build_targets ${target})
endforeach()
set("${out_var}" "${build_targets}" PARENT_SCOPE)
endfunction()
function(get_build_targets_in_directory out_var dirpath)
get_targets_in_directory(all_targets "${dirpath}")
filter_build_targets(filter_targets "${all_targets}")
set("${out_var}" "${filter_targets}" PARENT_SCOPE)
endfunction()
# 获取依赖包列表
function(get_build_pkg_deps out_var)
set(build_pkg_deps)
list(LENGTH ARGN len)
if(len GREATER 0)
math(EXPR stop "${len} - 1")
foreach(idx RANGE 0 ${stop} 2)
list(GET ARGN ${idx} pkg)
list(APPEND build_pkg_deps ${pkg})
endforeach()
endif()
set("${out_var}" "${build_pkg_deps}" PARENT_SCOPE)
endfunction()
function(do_get_pkg_dependencies pkgs all_pkgs)
set(pkgs_next)
foreach(pkg IN LISTS pkgs)
get_package_dir_by_package(pkg_dir "${pkg}")
include(${CANN_TOP_DIR}/${pkg_dir}/version.cmake)
get_build_pkg_deps(build_pkg_deps ${CANN_VERSION_${pkg}_BUILD_DEPS})
foreach(dep_pkg IN LISTS build_pkg_deps)
if(dep_pkg IN_LIST all_pkgs OR dep_pkg IN_LIST pkgs_next OR dep_pkg IN_LIST CANN_BINARY_PACKAGES OR CANN_BINARY_COMPONENTS_ALL)
continue()
endif()
if(dep_pkg STREQUAL "bisheng-compiler")
continue()
endif()
list(APPEND pkgs_next ${dep_pkg})
endforeach()
endforeach()
if(pkgs_next)
list(APPEND all_pkgs ${pkgs_next})
do_get_pkg_dependencies("${pkgs_next}" "${all_pkgs}")
endif()
set(all_pkgs "${all_pkgs}" PARENT_SCOPE)
endfunction()
# 获取依赖包及目录
function(get_pkg_dependencies pkgs)
do_get_pkg_dependencies("${pkgs}" "${pkgs}")
set(CANN_DEPEND_PACKAGES "${all_pkgs}" PARENT_SCOPE)
endfunction()
# 1. 添加cann_all_targets目标
# 2. 将其它目标标识为EXCLUDE_FROM_ALL,防止冗余编译
function(set_cann_all_targets pkg_dirs)
set(build_targets)
foreach(pkg_dir IN LISTS pkg_dirs)
get_build_targets_in_directory(pkg_targets ${CANN_TOP_DIR}/${pkg_dir})
list(APPEND build_targets ${pkg_targets})
endforeach()
get_build_targets_in_directory(all_targets "${CMAKE_SOURCE_DIR}")
foreach(target IN LISTS all_targets)
set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL TRUE)
endforeach()
add_custom_target(cann_all_targets ALL)
add_dependencies(cann_all_targets ${build_targets})
endfunction()
# 计算device编译相关参数
function(calc_device_packages)
set(DEVICE_CANN_PACKAGES)
set(DEVICE_CANN_DEPEND_PACKAGES)
foreach(PKG IN LISTS CANN_DEPEND_PACKAGES)
get_package_dir_by_package(PKG_DIR "${PKG}" "cmake/device")
if(IS_DIRECTORY "${CANN_TOP_DIR}/${PKG_DIR}")
list(APPEND DEVICE_CANN_DEPEND_PACKAGES ${PKG})
if(PKG IN_LIST CANN_PACKAGES)
list(APPEND DEVICE_CANN_PACKAGES ${PKG})
endif()
endif()
endforeach()
set(DEVICE_CANN_PACKAGES "${DEVICE_CANN_PACKAGES}" PARENT_SCOPE)
set(DEVICE_CANN_DEPEND_PACKAGES "${DEVICE_CANN_DEPEND_PACKAGES}" PARENT_SCOPE)
endfunction()
# 内部调用
function(get_project_deps TARGET_PROJ PROJ_ROOT_DIR OUT_VAR)
# -------------------------- 初始化变量 --------------------------
# 1. 初始化返回变量(仅首次调用时清空)
if(NOT DEFINED ${OUT_VAR})
set(${OUT_VAR} "" PARENT_SCOPE)
endif()
# 2. 初始化已处理项目列表(防循环依赖, 内部传递)
if(NOT DEFINED PROCESSED_PROJS)
set(PROCESSED_PROJS "")
endif()
# -------------------------- 检查循环依赖 --------------------------
list(FIND PROCESSED_PROJS "${TARGET_PROJ}" PROJ_INDEX)
if(NOT PROJ_INDEX EQUAL -1)
return() # 已处理过该项目, 直接返回
endif()
list(APPEND PROCESSED_PROJS "${TARGET_PROJ}")
# -------------------------- 定位dep.cmake --------------------------
# 目标项目的目录(绝对路径)
set(TARGET_PROJ_DIR "${PROJ_ROOT_DIR}/${TARGET_PROJ}")
# dep.cmake配置文件路径
set(DEP_CONFIG_FILE "${TARGET_PROJ_DIR}/cmake/deps.cmake")
# 无dep.cmake -> 无依赖, 直接返回
if(NOT EXISTS "${DEP_CONFIG_FILE}")
return()
endif()
# -------------------------- 解析dep.cmake --------------------------
# 临时变量存储当前项目的直接依赖
unset(CURRENT_DEPENDS)
unset(BUILD_DEPS)
# 加载dep.cmake, 读取DEPENDS变量
include("${DEP_CONFIG_FILE}")
# 检查DEPENDS是否定义(避免配置文件为空)
if(DEFINED BUILD_DEPS AND NOT BUILD_DEPS STREQUAL "")
# 拆分依赖列表(支持空格、分号分隔)
set(CURRENT_DEPENDS "${BUILD_DEPS}")
endif()
# -------------------------- 递归处理依赖 --------------------------
foreach(DEP_PROJ ${CURRENT_DEPENDS})
string(STRIP "${DEP_PROJ}" DEP_PROJ) # 去除空格
if(DEP_PROJ STREQUAL "")
continue()
endif()
if("${PRODUCT_SIDE}" STREQUAL "host")
set(DEP_PROJ_DIR "${PROJ_ROOT_DIR}/${DEP_PROJ}")
else()
set(DEP_PROJ_DIR "${PROJ_ROOT_DIR}/${DEP_PROJ}/cmake/device")
endif()
# 检查依赖目录是否存在
if(NOT IS_DIRECTORY "${DEP_PROJ_DIR}")
continue()
endif()
# 去重:避免重复添加同一目录
file(RELATIVE_PATH REL_PATH "${PROJ_ROOT_DIR}" "${DEP_PROJ_DIR}")
list(FIND ${OUT_VAR} "${REL_PATH}" DIR_INDEX)
if(DIR_INDEX EQUAL -1)
list(APPEND ${OUT_VAR} "${REL_PATH}")
set(${OUT_VAR} "${${OUT_VAR}}" PARENT_SCOPE)
# 递归解析当前依赖的子依赖
get_project_deps("${DEP_PROJ}" "${PROJ_ROOT_DIR}" "${OUT_VAR}" "${PROCESSED_PROJS}")
endif()
endforeach()
endfunction()
function(get_project_dep_dirs TARGET_PROJ PROJ_ROOT_DIR OUT_VAR)
# 清空返回变量, 避免残留
set(${OUT_VAR} "" PARENT_SCOPE)
# 调用核心递归函数
get_project_deps("${TARGET_PROJ}" "${PROJ_ROOT_DIR}" "PROJECT_DEPS")
# 结果打印输出
set(${OUT_VAR} "${PROJECT_DEPS}" PARENT_SCOPE)
endfunction()