CMake:目标不在导出集中

Ste*_*art 5 cmake

我有一个foo依赖于bar. bar安装后提供良好的效果barConfig.cmake。

cmake_minimum_required(VERSION 3.7)
project(foo VERSION 1.0)

find_package(bar)

add_library(foo SHARED foo.c)
target_link_libraries(foo PUBLIC bar)

install(TARGETS foo EXPORT fooTargets LIBRARY DESTINATION lib)
export(EXPORT fooTargets FILE fooTargets.cmake)
install(EXPORT fooTargets FILE fooTargets.cmake DESTINATION lib/cmake)
Run Code Online (Sandbox Code Playgroud)

这很好用。我们只需apt install bar-dev编译即可foo。

但由于这些包都是我们开发的并且是相关的,所以我的团队希望在同一个 IDE 会话中开发它们并同时编译它们。我想允许这样做,但我不想改变这样一个事实:这些已经作为单独的包部署并且可以独立构建。

if (DIRECTORY_EXISTS ../bar)
  set(BAR_NO_INSTALL ON)
  set(BAR_SKIP_TESTS ON)
  add_subdirectory(../bar ${CMAKE_CURRENT_BINARY_DIR}/bar)
else()
  find_package(bar)
endif()
Run Code Online (Sandbox Code Playgroud)

如果../bar/存在,则结果为:

CMake Error: install(EXPORT "fooTargets" ...) includes target "foo" which 
 requires target "bar" that is not in the export set.
Run Code Online (Sandbox Code Playgroud)

如何避免需要bar在foo包中导出?


我试图找出为什么find_package(bar)行得通,但add_subdirectory(bar)行不通。 barConfig.cmakeviabarTargets.cmake定义bar如下:

add_library(bar SHARED IMPORTED)
set_property(TARGET bar APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(bar PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "../bar/include"
  IMPORTED_LOCATION_NOCONFIG "build/libbar.so.1.0.13574"
  IMPORTED_SONAME_NOCONFIG "libbar.so.1"
)
Run Code Online (Sandbox Code Playgroud)

我的猜测是在自动生成IMPORTED的. 我尝试添加设置属性(如下),但没有发现任何差异:barbarConfig.cmake

set_target_properties(bar PROPERTIES IMPORTED TRUE)
Run Code Online (Sandbox Code Playgroud)

fooConfig.cmake确实包括创建目标的以下内容bar(不是bar::bar)。

find_depdendency(bar)
Run Code Online (Sandbox Code Playgroud)

我尝试了以下别名,希望别名无法导出对我有帮助。但它仍然期望我导出bar。

add_library(barImported ALIAS bar)
target_link_libraries(foo PUBLIC barImported)
Run Code Online (Sandbox Code Playgroud)

我尝试仅链接构建接口,希望bar在安装时不需要导出。但这也没有效果。

target_link_libraries(foo PUBLIC $<BUILD_INTERFACE:bar>)
Run Code Online (Sandbox Code Playgroud)

Ste*_*art 2

这是我想出的解决方案。这适用于任意库foo(动态和静态版本加上测试),它也可以以与我导入相同的方式成为依赖项bar:

cmake_minimum_required(VERSION 3.7)
include(GNUInstallDirs)

project(foo VERSION 1.0)

# Options set to 
#  ON  explicitly for build machines, 
#  OFF by default for developers who build dependencies inline and only build for local use
option(FOO_EXPORT_TARGETS "Generate fooConfig.cmake for packaging." OFF)
option(FOO_BUILD_TESTS "Build and run tests. Default off for aircraft. Turn on in your IDE if developing foo" OFF)

# This is best implemented as a macro/function from an included cmake
set(BAR_ROOT "/noexist" CACHE PATH "Location of BAR for in-source building")
if (DIRECTORY_EXISTS "${BAR_ROOT}")
  add_subdirectory(${BAR_ROOT})
else()
  find_package(bar)
endif()

set(fooSources foo.c)
set(fooPublicHeaders include/foo.h)

add_library(fooObjects OBJECT ${fooSources})
target_include_directories(fooObjects
  PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
  PRIVATE
    $<TARGET_PROPERTY:bar,INTERFACE_INCLUDE_DIRECTORIES>
)

add_library(foo       SHARED $<TARGET_OBJECTS:fooObjects>)
add_library(fooStatic STATIC $<TARGET_OBJECTS:fooObjects>)
target_link_libraries(foo       PUBLIC fooObjects bar)
target_link_libraries(fooStatic PUBLIC fooObjects barStatic)

set_target_properties(fooObjects foo fooStatic
  PROPERTIES
    POSITION_INDEPENDENT_CODE ON
)

if (NOT FOO_EXPORT_TARGETS)
  install(TARGETS foo LIBRARY DESTINATION lib)
else()
  set_target_properties(foo fooStatic
    PROPERTIES
      VERSION ${foo_VERSION}
      SOVERSION ${foo_VERSION_MAJOR}
      PUBLIC_HEADER "${fooPublicHeaders}"
  )

  install(
    TARGETS foo fooStatic fooObjects
    EXPORT fooTargets
    RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/foo
  )

  include(CMakePackageConfigHelpers)

  set(configDir "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/foo-${foo_VERSION_MAJOR}")

  configure_package_config_file(
    "${CMAKE_CURRENT_LIST_DIR}/fooConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
    INSTALL_DESTINATION ${configDir}
  )

  write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
    VERSION ${foo_VERSION}
    COMPATIBILITY SameMajorVersion
  )

  install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
    DESTINATION ${configDir}
  )

  export (EXPORT fooTargets FILE fooTargets.cmake)
  install(EXPORT fooTargets FILE fooTargets.cmake DESTINATION ${configDir})
endif()

if (FOO_BUILD_TESTS)
  add_executable(test_foo test.c)
  target_link_libraries(test_foo foo)
  
  enable_testing()
  add_test(NAME test_foo COMMAND test_foo WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test)
endif()
Run Code Online (Sandbox Code Playgroud)

具有以下内容fooConfig.cmake.in:

include(CMakeFindDependencyMacro)

@PACKAGE_INIT@

find_dependency(bar)

if(NOT TARGET foo)
  include("${CMAKE_CURRENT_LIST_DIR}/fooTargets.cmake")
endif()
Run Code Online (Sandbox Code Playgroud)

构建机器由此创建一个 debian 包。这就是debian/rules文件的样子,以确保它是在没有任何源内构建、打包和测试的情况下进行编译的:

#!/usr/bin/make -f
#export DH_VERBOSE = 1

%:
    dh $@

override_dh_auto_configure:
    dh_auto_configure -- \
        -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \
        -DFOO_BUILD_TESTS=ON \
        -DFOO_EXPORT_TARGETS=ON \
        -DBAR_ROOT=/noexist
Run Code Online (Sandbox Code Playgroud)