如何避免在CMake中创建包时必须指定两次公共依赖项?

new9mtju  于 2022-11-11  发布在  其他
关注(0)|答案(1)|浏览(165)

目前在理解用CMake创建软件包的正确方法方面有一些困难。
我尝试用各种编译选项构建一个库,并将其用作可重定位的包。我有一个类似于下面的CMakeLists.txt

add_library(Lib lib.cpp lib.h)

if (WITH_FOO)
    find_package(Foo REQUIRED)
    target_link_libraries(Lib Foo::Foo)
endif()

if (WITH_BAR)
    find_package(Bar REQUIRED)
    target_link_libraries(Lib Bar::Bar)
endif()

if (WITH_OTHER_TARGET)
    add_library(OtherTarget other_target.cpp other_target.h)
    target_link_libraries(Lib OtherTarget)
endif()

include(GNUInstallDirs)
install(TARGETS Lib
    EXPORT LibTargets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(EXPORT LibTargets
    FILE LibTargets.cmake
    NAMESPACE Lib::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Lib
)

include(CMakePackageConfigHelpers)

configure_package_config_file(
    LibConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/LibConfig.cmake
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Lib
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LibConfig.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Lib
)

据我所知,所有公共依赖项都应该列在LibConfig.cmake文件中。
正确的方法是什么?我可以这样做:

@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/LibTargets.cmake")

include(CMakeFindDependencyMacro)

if (@WITH_FOO@)
    find_dependency(Foo)
endif()

if (@WITH_BAR@)
    find_dependency(Bar)
endif()

但这似乎很容易出错,因为我必须记住为每一个新添加的公共依赖项添加find_dependency
我还尝试使用Lib目标的INTERFACE_LINK_LIBRARIES属性,但这似乎相当不可靠,因为它与我自己的目标和其他东西(如-lpthread)混杂在一起。
我有点迷路了。做这件事的正确方法是什么?我做这件事全错了吗?

5cnsuln7

5cnsuln71#

是的,在项目的CMakeLists.txt中的每个find_package调用的配置文件中有find_dependency调用是正确的。
如果您有许多条件find_package调用,并且希望避免在配置文件中进行相应的条件find_dependency调用,那么您可以准备一些变量,用于“联机”收集所有这些调用。


# This variable will contain list of `find_dependency` calls to be added into the config file.

set(config_dependencies)

if (WITH_FOO)
    find_package(Foo REQUIRED)
    # Add corresponding `find_dependency` into the config file
    set(config_dependencies "${config_dependencies}\nfind_dependency(Foo)")

    target_link_libraries(Lib Foo::Foo)
endif()

if (WITH_BAR)
    find_package(Bar REQUIRED)
    # Add corresponding `find_dependency` into the config file
    set(config_dependencies "${config_dependencies}\nfind_dependency(Bar)")

    target_link_libraries(Lib Bar::Bar)
endif()

因此,在配置文件的模板中,您将具有


# ...

include(CMakeFindDependencyMacro)

@config_dependencies@

可以使用GLOBAL属性来代替变量,该属性可以从任何作用域更新,而不仅仅是从顶层CMakeLists.txt


# This variable will contain list of `find_dependency` calls to be added into the config file.

set_property(GLOBAL PROPERTY config_dependencies "")

# Convenient function for add find_dependency call into the property

# Usage:

# 

# add_find_dependency(package_name [args...])

function(add_find_dependency package_name)
  # Combine package name and other parameters into the space-separated string.
  string(JOIN " " args_string ${package_name} ${ARGN})
  set_property(GLOBAL APPEND_STRING PROPERTY config_dependencies
    "\nfind_dependency(${args_string})"
  )
endfunction()

if (WITH_FOO)
    find_package(Foo REQUIRED)
    add_find_dependency(Foo)
    target_link_libraries(Lib Foo::Foo)
endif()

if (WITH_BAR)
    find_package(Bar REQUIRED)
    add_find_dependency(Bar)
    target_link_libraries(Lib Bar::Bar)
endif()

# ... from inner `CMakeLists.txt`

if (WITH_BAZ)
    find_package(Baz REQUIRED COMPONENTS aaa bbb)
    add_find_dependency(Baz COMPONENTS aaa bbb)
    target_link_libraries(Lib Baz::Baz)
endif()

# ...

# Prepare variables for creating the config file

get_property(config_dependencies GLOBAL PROPERTY config_dependencies)

configure_package_config_file(...)

配置文件的模板将相同:


# ...

include(CMakeFindDependencyMacro)

@config_dependencies@

相关问题