将包含自定义目标的目录导出到消费者

Posted

技术标签:

【中文标题】将包含自定义目标的目录导出到消费者【英文标题】:Export include directories of custom target to consumer 【发布时间】:2021-01-14 12:57:48 【问题描述】:

我在my-library/ 有一个独立的库,它有一个自定义目标,可以通过外部命令生成一些标头。为了将包含这些标头的目录从父目录导出为消耗品,我想执行以下操作:

add_custom_target(my-target ALL
    COMMAND ...
)
target_include_directories(my-target
    INTERFACE my_include_directory/
)

并从父项目中使用它:

add_subdirectory(my-library)
add_executable(consumer ...)
add_dependencies(consumer my-target)

这在语义上对我来说是正常的,my_include_directory/consumer 是可见的。然而,这会导致:

target_include_directories called with non-compliant target type

我能想到的最佳解决方案是将我的库中的包含目录导出为变量:

add_custom_target(my-target ALL
    COMMAND ...
)
set(MY_TARGET_INCLUDE_DIRS my_include_directory/ PARENT_SCOPE)

并通过此变量使用包含目录:

add_subdirectory(my-library)
add_executable(consumer ...)
add_dependencies(consumer my-target)
target_include_directories(consumer PRIVATE $MY_TARGET_INCLUDE_DIRS)

这并不理想。有更好的解决方案还是我看错了?

【问题讨论】:

只需创建 IMPORTED 库目标(比如my_lib),它包含编译和链接所需的库的所有信息:库文件、包含目录等。所以你可以使用add_executable(consumer my_lib) 链接你的库并使用它的所有属性。可以指定 IMPORTED 库目标和创建库文件和标头的自定义目标之间的依赖关系:add_dependencies(my_lib my-target). 【参考方案1】:

如果您想使用主项目构建库,那么您可能希望将生成的标头公开为interface library。这样,消费目标就不会关心或知道生成了标头,而是像往常一样使用target_link_libraries()。 您也可能不想使用add_custom_target() 来生成标题。根据文档:

目标没有输出文件,总是被认为是过时的

这意味着在构建时总是会生成标题。而不是只在输入发生变化时构建。

以下是使用add_custom_command() 生成标头并通过add_custom_target() 连接到interface library 的示例。

my_library/CMakeLists.txt

set(INPUT_FILE "$CMAKE_CURRENT_LIST_DIR/header.in")
set(GENERATED_HEADER "$CMAKE_CURRENT_BINARY_DIR/some_header.h")

add_custom_command(
    OUTPUT $GENERATED_HEADER
    COMMAND $CMAKE_COMMAND -E copy $INPUT_FILE $GENERATED_HEADER
    DEPENDS $INPUT_FILE
    )

# Per the docs for add_custom_command, we use a custom target depending
# on the generated header to ensure it is only generated at most once per
# build
add_custom_target(local_target DEPENDS $GENERATED_HEADER)

add_library(my_library INTERFACE)
target_include_directories(my_library INTERFACE $CMAKE_CURRENT_BINARY_DIR)

# Per docs for add_dependencies
#
#   Dependencies added to an imported target or an interface library are
#   followed transitively
# 
# This ensures that the `local_target` will be built prior to anything that may end
# up using `my_library`.
add_dependencies(my_library local_target)

my_library/header.in

#define SOME_DEFINE 4

./CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(example)

add_subdirectory(my_library)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE my_library)

./main.cpp

#include <stdio.h>

#include "some_header.h"

int main(int argc, char *argv[]) 
    printf("The value is %d.\n", SOME_DEFINE);

【讨论】:

【参考方案2】:

假设您分发二进制文件,只需为目录添加install 指令,并确保通过INCLUDES DESTINATIONinstall(TARGETS) 列出您安装标头的目录:

add_library(my-library ...)

add_custom_target(my-target ALL
    COMMAND ...
)

set(_INCLUDE_INSTALL_DIR my-library/includes)

add_dependencies(my-library my-target)
install(DIRECTORY "$CMAKE_CURRENT_BINARY_DIR/generated-includes/" DESTINATION $_INCLUDE_INSTALL_DIR)
install(TARGETS my-library EXPORT my-library
    LIBRARY DESTINATION my-library/lib
    INCLUDES DESTINATION $_INCLUDE_INSTALL_DIR)

install(EXPORT my-library DESTINATION my-library/lib/cmake FILE my-library-config.cmake)

通过这种方式,您应该获得一个配置脚本以与可与find_package 一起使用的lib 一起部署以获取导入的目标my-library(假设my-library 安装的目录或其父目录位于CMAKE_PREFIX_PATH 中)。

find_package(my-library REQUIRED)

add_executable(consumer ...)
target_link_libraries(consumer PRIVATE my-library)

【讨论】:

以上是关于将包含自定义目标的目录导出到消费者的主要内容,如果未能解决你的问题,请参考以下文章

生产者和消费者的 Kafka Metrics 导出器

采用多线程和生产者消费者模式来实现对于一个目录以及所有子目录下的文件内容的搜索,打印出包含指定关键字的行.

dubbo 自定义异常

客快物流大数据项目(六十一):将消费的kafka数据同步到Kudu中

大数据批量处理神器 - 自定义周期批量消费队列的实现

Facebook中的自定义动词