如何从目标获取包含目录以在add_custom_target中使用?

12

我正在使用 target_link_libraries 对依赖进行建模,就像这篇博客文章中所做的那样。

target_link_libraries(Foo
    LibraryA
    LibraryB
)

这个方法很好用,但由于各种原因我需要使用add_custom_target通过自定义命令预处理一个文件。问题是,这个自定义目标依赖于LibraryA和LibraryB的包含文件。我真的希望像target_link_libraries一样实现以下内容(请参见LibraryA和LibraryB部分):

add_custom_target(Bar ALL
    COMMAND ${CMAKE_C_COMPILER} thing.cpp LibraryA LibraryB /P
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Path/Here
    COMMENT "Preprocessing to a file"
    VERBATIM
)

然而,这样做不起作用。 LibraryA和LibraryB按照它们的样子被放入其中。 即使它起作用了,我想我会得到更多的包括,因为我认为目标也包括库。也许这不是一个好方法。
那么,在这里我该怎么办?我如何从每个目标中提取include目录以供自定义命令使用? 我发现如果我使用find_package(Foo REQUIRED),我可以访问Foo_DIR,但它指向构建目录而不是包含目录的源目录。

你的目标是从 LibraryA 和 LibraryB 的目标中提取包含目录,并在自定义命令中使用这些目录吗? - Kevin
@squareskittles 正确。我只需要从LibraryA和LibraryB中的头文件。 - Stradigos
你在自定义命令中如何使用它们?如果 CMAKE_C_COMPILERgcc,你是否打算将它们作为包含目录传递给编译器?使用 -I 标志吗? - Kevin
1
这是我试图找出的一部分。如果我有一个变量可以提供路径,我可以使用Include标志将它们包含到自定义命令中。如果我无法获得它,因为我正在使用目标建模依赖关系,也许有些事情我可以做。我只是不知道该怎么做。 - Stradigos
2个回答

17
您可以使用 get_target_property() 从每个目标中提取包含目录。目标的 INCLUDE_DIRECTORIES 属性包含该目标的包含目录。由于您有两个目标,LibraryALibraryB,我们需要调用它两次。然后,我们可以使用 foreach() 将包含目录列表连接在一起。如果您在编译器命令(如 MSVC)中将这些作为包含目录使用,则还可以在循环中每个目录后面添加 /I 编译器选项
# Get the include directories for the target.
get_target_property(LIBA_INCLUDES LibraryA INCLUDE_DIRECTORIES)
get_target_property(LIBB_INCLUDES LibraryB INCLUDE_DIRECTORIES)

# Construct the compiler string for the include directories.
foreach(dir ${LIBA_INCLUDES} ${LIBB_INCLUDES})
    string(APPEND INCLUDE_COMPILER_STRING "/I${dir} ")
endforeach()

接着,您可以使用已构建的INCLUDE_COMPILER_STRING变量调用自定义目标命令:

add_custom_target(Bar ALL
    COMMAND ${CMAKE_C_COMPILER} thing.cpp ${INCLUDE_COMPILER_STRING} /P
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Path/Here
    COMMENT "Preprocessing to a file"
    VERBATIM
)
如果您想要更简洁的内容,可以使用生成器表达式示例此处,它获取目标的包含目录并在自定义目标命令中内联扩展它们。也可以尝试以下代码:
add_custom_target(Bar ALL
    COMMAND ${CMAKE_C_COMPILER} thing.cpp 
        "/I$<JOIN:$<TARGET_PROPERTY:LibraryA,INCLUDE_DIRECTORIES>,;/I>"
        "/I$<JOIN:$<TARGET_PROPERTY:LibraryB,INCLUDE_DIRECTORIES>,;/I>"
        /P
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Path/Here
    COMMENT "Preprocessing to a file"
    VERBATIM
    COMMAND_EXPAND_LISTS
)

在上面的示例中,我遇到了LIBA_INCLUDES-NOTFOUND错误,只有使用find_package(LibraryA REQUIRED)才能发现这个问题。如果不使用find_package(),则无法找到目标,从而导致CMake错误。我正在使用LibraryAConfig.cmake.in文件,在其中调用find_dependency(LibraryA REQUIRED)。我尝试在那里设置变量并从CMakeLists中打印它,但是该变量要么未设置,要么未传播,因为我测试消息为空。 - Stradigos
我用简洁版本运行时有了一些好运,但问题是使用目标会引入多个包含目录,并将它们放在一个引号集合内。自然地,编译器将其视为一个大的包含路径,而不是三个单独的路径。我尝试过只尝试在JOIN命令周围加上引号,但JOIN结果变得混乱不堪。 - Stradigos
修复了!您的行应该是"/I$<JOIN:$<TARGET_PROPERTY:LibraryA,INCLUDE_DIRECTORIES>,;/I>"。分号非常重要。我还添加了COMMAND_EXPAND_LISTS。请更新您的答案!感谢您的帮助,这很棒! - Stradigos
1
@Stradigos 我不知道你在使用find_package()来获取那些库,所以在这种情况下,get_target_property()可能无法按预期工作。还有抱歉!我忘记在最后一个示例中添加COMMAND_EXPAND_LISTS列表部分。根据你的反馈,我更新了答案。很高兴它能够正常工作! - Kevin
2
这不处理传递性依赖关系。 - Lars Bilke

2

正如评论所述,当前被接受的答案无法处理传递依赖项。这个问题让我一整天都很困惑,所以我现在要梳理一下。

我正在构建LibraryLinkUtilities 这里。这是我在项目中使用的CMakeLists.txt:

cmake_minimum_required(VERSION 3.15.0)

project ("CMakeProject1")

set(LLU_ROOT "D:/test/LibraryLinkUtilities/install")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)

find_package(LLU NO_MODULE PATH_SUFFIXES LLU)

add_library(${PROJECT_NAME} SHARED ${PROJECT_NAME}.cpp)

target_link_libraries(${PROJECT_NAME} PRIVATE LLU::LLU)

当我用Visual Studio打开.sln文件时,一切都很正常,不管是哪种构建类型都可以。但是我发现在配置包含目录为空。这让我很疑惑,因为我想知道项目实际上包括哪些目录。然后我使用print_target_properties函数来修复此处,以打印有关导入目标的所有属性:

function(print_target_properties target)
    if(NOT TARGET ${target})
      message(STATUS "There is no target named '${target}'")
      return()
    endif()

    foreach(property ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "DEBUG" property ${property})

        get_property(was_set TARGET ${target} PROPERTY ${property} SET)
        if(was_set)
            get_target_property(value ${target} ${property})
            message("${target} ${property} = ${value}")
        endif()
    endforeach()
endfunction()

print_target_properties(LLU::LLU)

enter image description here

请注意红线处,LLU::LLU 依赖于 WSTP::WSTPWolframLibrary::WolframLibrary。因此我使用以下代码打印所有包含目录:
include(CMakePrintHelpers)
get_target_property(LLUDEPENDS LLU::LLU INTERFACE_LINK_LIBRARIES)
cmake_print_properties(TARGETS LLU::LLU ${LLUDEPENDS} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接