You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake自定义命令获取依赖库递归包含目录失败,如何编译CUDA Fatbin?

问题:提取INTERFACE库递归包含目录失败,无法正确编译CUDA Fatbin

尝试编写CMake函数,结合所有INTERFACE类型依赖库的包含路径编译CUDA Fatbin文件,但提取依赖的递归包含目录时出错,输出如下:

--  print_all_include_directories lib DummyCompile_cuda_INPUT_fatbin_offline.cu
-- print_all_include_directories_internal Include directories for target DummyCompile_cuda_INPUT_fatbin_offline.cu:
INCLUDE_DIRS INCLUDE_DIRS-NOTFOUND
-- print_all_include_directories_internal Include directories for target ParamL:
INCLUDE_DIRS INCLUDE_DIRS-NOTFOUND

预期应获取依赖库路径(如/.../AnotherFolder),需定位问题原因并找到替代方案,以结合所有包含目录编译Fatbin。


相关代码

主CMake脚本

cmake_minimum_required(VERSION 3.29)
project(TestJitLto CUDA)
set(CMAKE_CUDA_ARCHITECTURES 86 75 70 )
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_CUDA_STANDARD 17)
find_package(CUDAToolkit REQUIRED cusparse cublas cudadevrt cudart nvJitLink)

add_executable(TestJitLto online.cu)
set_target_properties(TestJitLto PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_link_libraries(TestJitLto PUBLIC CUDA::nvJitLink  CUDA::cudart  CUDA::nvrtc)

ADD_SUBDIRECTORY(AnotherFolder)
ADD_SUBDIRECTORY(JitLtoLib)

Fatbin生成CMake脚本

##Function to recursively get and print include directories of all linked libraries
function(print_all_include_directories_internal target)
 message(STATUS "print_all_include_directories_internal Include directories for target ${target}:")

 # Get the include directories of the target itself
 get_target_property(INCLUDE_DIRS ${target} INTERFACE_INCLUDE_DIRECTORIES)
 get_target_property(INCLUDE_DIRS ${target} INCLUDE_DIRECTORIES)
 message("INCLUDE_DIRS ${INCLUDE_DIRS}")
 if (INCLUDE_DIRS)
  foreach (dir ${INCLUDE_DIRS})
   list(APPEND INCLUDE_LIST_TMP ${dir})
  endforeach ()
  list(APPEND INCLUDE_LIST_TMP ${INCLUDE_LIST})
  list(REMOVE_DUPLICATES INCLUDE_LIST_TMP)
  message("INCLUDE_LIST_TMP ${INCLUDE_LIST_TMP}")
  set(INCLUDE_LIST2 ${INCLUDE_LIST_TMP})
 else ()
  set(INCLUDE_LIST2 "")
 endif ()
 # Now get the libraries linked to this target
 get_target_property(LINK_LIBS ${target} LINK_LIBRARIES)
 #get_target_property(LINK_LIBS2 ${target} INTERFACE_LINK_LIBRARIES)

 set(LINK_LIBS ${LINK_LIBS} ${LINK_LIBS2})
 list(REMOVE_DUPLICATES LINK_LIBS)
 #message("LINK_LIBS ${LINK_LIBS}")
 if (LINK_LIBS)
  foreach (linked_lib ${LINK_LIBS})
   # Check if the linked library is a target, skip if it's a system library or non-target
   if (TARGET ${linked_lib})
    #message(STATUS "Checking include directories for linked library: ${linked_lib}  ${INCLUDE_LIST}")
    print_all_include_directories_internal(${linked_lib})  # Recursive call
    list(APPEND INCLUDE_LIST2 ${INCLUDE_LIST})
    list(REMOVE_DUPLICATES INCLUDE_LIST2)
   else ()
    list(APPEND INCLUDE_LIST2 ${INCLUDE_LIST})
    list(REMOVE_DUPLICATES INCLUDE_LIST2)
    # message(STATUS "INCLUDE_LIST1:" ${INCLUDE_LIST2})
   endif ()
  endforeach ()
 else ()
  list(APPEND INCLUDE_LIST2 ${INCLUDE_LIST})
  list(REMOVE_DUPLICATES INCLUDE_LIST2)
  #message(STATUS "INCLUDE_LIST2:" ${INCLUDE_LIST2})
 endif ()

 set(INCLUDE_LIST ${INCLUDE_LIST2} PARENT_SCOPE)
endfunction()

function(print_all_include_directories lib)
 message(STATUS " print_all_include_directories lib ${lib}")
 print_all_include_directories_internal(${lib})
 set(INCLUDE_LIST ${INCLUDE_LIST} PARENT_SCOPE)
 message(STATUS " print_all_include_directories1 ${INCLUDE_LIST}")
endfunction()


# Function to compile a single CUDA file into a fatbin
function(compile_cuda_fatbin_function INPUT_FILE OUTPUT_DIR DEPENDS_LIBS)

 ADD_LIBRARY(DummyCompile_cuda_INPUT_fatbin_${INPUT_FILE}  ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_FILE})
 target_link_libraries(DummyCompile_cuda_INPUT_fatbin_${INPUT_FILE} PUBLIC ${DEPENDS_LIBS})

 print_all_include_directories(DummyCompile_cuda_INPUT_fatbin_${INPUT_FILE})

 get_filename_component(OUT_FILE ${INPUT_FILE} NAME_WLE)
 message("CMAKE_CUDA_ARCHITECTURES  ${CMAKE_CUDA_ARCHITECTURES}")

 # Initialize an empty list for the flags
 set(GENCODE_FLAGS "")

 # Split the architecture string by semicolons and iterate over each
 foreach (ARCH IN LISTS CMAKE_CUDA_ARCHITECTURES)

  # Add `-gencode` flags to the list
  list(APPEND GENCODE_FLAGS "-gencode arch=compute_${ARCH},code=lto_${ARCH}")
 endforeach ()
 string(REPLACE ";" " " GENCODE_FLAGS "${GENCODE_FLAGS}")
 # Print the generated flags for verification
 message(STATUS "Generated -gencode flags1: ${GENCODE_FLAGS}")

 message("ARCH_VECTOR is ${GENCODE_FLAGS}")
 separate_arguments(GENCODE_FLAGSA UNIX_COMMAND "${GENCODE_FLAGS}")
 # Custom command to compile the CUDA file into a fatbin
 add_custom_command(
   OUTPUT "${OUTPUT_DIR}/${INPUT_FILE}.fatbin"
   VERBATIM
   COMMAND /usr/local/cuda/bin/nvcc
   ARGS ${GENCODE_FLAGSA} -rdc=true -fatbin ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUT_FILE}.fatbin
   MAIN_DEPENDENCY  "${INPUT_FILE}"
   COMMENT "Compiling CUDA binary for ${INPUT_FILE} to fatbin with LTO archive"
 )

 # Custom target that depends on the fatbin output
 add_custom_target(compile_cuda_fatbin_${INPUT_FILE}
   DEPENDS "${OUTPUT_DIR}/${INPUT_FILE}.fatbin" ${DEPENDS_LIBS}
 )


 ADD_LIBRARY(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE)
 target_link_libraries(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE ${DEPENDS_LIBS})
 target_include_directories(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
 add_dependencies(Dummycompile_cuda_fatbin_${INPUT_FILE} compile_cuda_fatbin_${INPUT_FILE})
endfunction()

# Function to compile a list of CUDA files into fatbins
function(compile_cuda_fatbin_files_function FILES)
 foreach (FILE ${FILES})
  compile_cuda_fatbin_function("${FILE}" "${OUTPUT_DIR}" "${LTO_ARC}")
 endforeach ()
endfunction()

compile_cuda_fatbin_function(offline.cu ${CMAKE_CURRENT_SOURCE_DIR} ParamL)

依赖库CMake脚本(AnotherFolder)

add_library(ParamL INTERFACE)
target_include_directories(ParamL INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

问题原因分析

  1. 变量覆盖错误:在print_all_include_directories_internal函数中,先获取INTERFACE_INCLUDE_DIRECTORIES,随后立即获取INCLUDE_DIRECTORIES并覆盖INCLUDE_DIRS变量。INTERFACE类型目标仅INTERFACE_INCLUDE_DIRECTORIES有有效路径,INCLUDE_DIRECTORIES会返回NOTFOUND,导致正确路径被覆盖丢失。
  2. 目标属性匹配错误:创建的DummyCompile_cuda_INPUT_fatbin_${INPUT_FILE}是普通库目标,无法自动继承依赖INTERFACE库的包含路径,直接获取INCLUDE_DIRECTORIES无法得到INTERFACE库的路径。
  3. 递归变量传递逻辑混乱:递归调用时,INCLUDE_LIST的父作用域传递逻辑存在问题,子函数的结果无法正确合并到父函数中,导致最终无法收集到完整路径。

解决方案

方法1:修复递归提取函数

修改print_all_include_directories_internal函数,区分目标类型并修正变量传递逻辑:

function(print_all_include_directories_internal target)
    message(STATUS "print_all_include_directories_internal Include directories for target ${target}:")
    
    set(INCLUDE_LIST_TMP "")
    # 根据目标类型选择对应属性
    get_target_property(TARGET_TYPE ${target} TYPE)
    if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY")
        get_target_property(INCLUDE_DIRS ${target} INTERFACE_INCLUDE_DIRECTORIES)
    else()
        get_target_property(INCLUDE_DIRS ${target} INCLUDE_DIRECTORIES)
        # 同时获取目标自身的INTERFACE包含路径
        get_target_property(INTERFACE_INCLUDES ${target} INTERFACE_INCLUDE_DIRECTORIES)
        if(INTERFACE_INCLUDES)
            list(APPEND INCLUDE_DIRS ${INTERFACE_INCLUDES})
        endif()
    endif()

    if(INCLUDE_DIRS)
        list(APPEND INCLUDE_LIST_TMP ${INCLUDE_DIRS})
        list(REMOVE_DUPLICATES INCLUDE_LIST_TMP)
        message("INCLUDE_DIRS ${INCLUDE_DIRS}")
    endif()

    # 获取所有链接库,包括INTERFACE_LINK_LIBRARIES
    get_target_property(LINK_LIBS ${target} LINK_LIBRARIES)
    get_target_property(INTERFACE_LINK_LIBS ${target} INTERFACE_LINK_LIBRARIES)
    set(ALL_LINK_LIBS ${LINK_LIBS} ${INTERFACE_LINK_LIBS})
    list(REMOVE_DUPLICATES ALL_LINK_LIBS)

    if(ALL_LINK_LIBS)
        foreach(linked_lib ${ALL_LINK_LIBS})
            if(TARGET ${linked_lib})
                print_all_include_directories_internal(${linked_lib})
                # 合并子函数返回的包含目录
                list(APPEND INCLUDE_LIST_TMP ${INCLUDE_LIST})
                list(REMOVE_DUPLICATES INCLUDE_LIST_TMP)
            endif()
        endforeach()
    endif()

    set(INCLUDE_LIST ${INCLUDE_LIST_TMP} PARENT_SCOPE)
endfunction()

方法2:利用CMake生成器表达式(更推荐)

无需手动递归提取,通过CMake原生生成器表达式自动获取所有依赖的包含目录,简化实现:

function(compile_cuda_fatbin_function INPUT_FILE OUTPUT_DIR DEPENDS_LIBS)
    # 创建INTERFACE目标聚合所有依赖
    add_library(FatbinDeps_${INPUT_FILE} INTERFACE)
    target_link_libraries(FatbinDeps_${INPUT_FILE} INTERFACE ${DEPENDS_LIBS})

    # 生成包含目录的-I参数
    set(INCLUDE_FLAGS "")
    # 使用生成器表达式递归获取所有INTERFACE包含目录
    string(APPEND INCLUDE_FLAGS "$<JOIN:$<TARGET_PROPERTY:FatbinDeps_${INPUT_FILE},INTERFACE_INCLUDE_DIRECTORIES>, -I>")
    if(INCLUDE_FLAGS)
        set(INCLUDE_FLAGS "-I${INCLUDE_FLAGS}")
    endif()

    get_filename_component(OUT_FILE ${INPUT_FILE} NAME_WLE)
    set(GENCODE_FLAGS "")
    foreach(ARCH IN LISTS CMAKE_CUDA_ARCHITECTURES)
        list(APPEND GENCODE_FLAGS "-gencode arch=compute_${ARCH},code=lto_${ARCH}")
    endforeach()

    add_custom_command(
        OUTPUT "${OUTPUT_DIR}/${INPUT_FILE}.fatbin"
        VERBATIM
        COMMAND $<TARGET_FILE:CUDA::nvcc>
        ARGS ${GENCODE_FLAGS} -rdc=true -fatbin ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_FILE} 
             ${INCLUDE_FLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUT_FILE}.fatbin
        MAIN_DEPENDENCY "${INPUT_FILE}"
        COMMENT "Compiling CUDA binary for ${INPUT_FILE} to fatbin with LTO archive"
    )

    add_custom_target(compile_cuda_fatbin_${INPUT_FILE}
        DEPENDS "${OUTPUT_DIR}/${INPUT_FILE}.fatbin"
    )

    add_library(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE)
    target_link_libraries(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE ${DEPENDS_LIBS})
    target_include_directories(Dummycompile_cuda_fatbin_${INPUT_FILE} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
    add_dependencies(Dummycompile_cuda_fatbin_${INPUT_FILE} compile_cuda_fatbin_${INPUT_FILE})
endfunction()

关键改进说明

  • 方法2通过$<TARGET_PROPERTY:...,INTERFACE_INCLUDE_DIRECTORIES>生成器表达式,自动递归获取所有依赖INTERFACE库的包含路径,避免手动遍历的错误。
  • 使用$<TARGET_FILE:CUDA::nvcc>替代硬编码路径,提升跨平台兼容性。
  • 聚合INTERFACE目标统一管理依赖,确保包含路径正确传递。

内容的提问来源于stack exchange,提问作者Yehonatan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:42:31