【问题标题】:CMake - including dependencies inside a static libraryCMake - 包括静态库中的依赖项
【发布时间】:2018-02-11 22:46:31
【问题描述】:

我对 C++ 以及所有相关的术语和工具链非常陌生。我正在尝试构建一个客户可以在自己的项目中使用的静态库。理想情况下,我只想向他们发送一个 .a 和一个 .lib 文件,以及一个 .h 文件。

现在,我的 CMake 文件如下所示:

project(ava-engine-client)
cmake_minimum_required(VERSION 3.9.6)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a )

add_compile_options(-std=c++11)

# GRPC and Protocol Buffers libraries location
list(APPEND CMAKE_PREFIX_PATH "/opt/grpc" "/opt/protobuf")

# CMake find modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")


# Recurse and find all the generated Protobuf .cc files
file(GLOB_RECURSE PROTO_GEN_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ava_engine/ava/*.cc)

include_directories("${CMAKE_CURRENT_SOURCE_DIR}")

# Building the library
add_library(ava_engine_client STATIC src/AvaEngineClient.cc src/AvaEngineClient.h ${PROTO_GEN_SRCS})

target_link_libraries(ava_engine_client ${PROTOBUF_LIBRARIES} ${GRPC_LIBRARY})


## Building Playground
add_executable(playground src/Playground.cc)

target_link_libraries(playground ava_engine_client)

现在这在链接阶段失败了,因为我没有将游乐场目标与 ava_engine_client 库中的依赖项链接:

Undefined symbols for architecture x86_64:
  "grpc::ClientContext::ClientContext()", referenced from:
      ...
  "grpc::ClientContext::~ClientContext()", referenced from:
      ...
  "grpc::CreateChannel(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<grpc::ChannelCredentials> const&)", referenced from:
      ...
  "grpc::g_core_codegen_interface", referenced from:
      ...

这不是我想要的,因为它需要客户链接到我的库中的依赖项(这对我来说似乎不正确)。

现在,我已经阅读了一些 StackOverflow 帖子,例如:(CMake: include library dependencies in a static library),其中建议使用 CMAKE_CXX_ARCHIVE_CREATE 创建存档文件。 这是我应该采取的方法吗?我想要的有可能吗?

【问题讨论】:

  • 您还需要发送一个 .h 文件。
  • 是的,对不起,我忘了包括那个。
  • set (CMAKE_CXX_STANDARD 11)代替这个add_compile_options(-std=c++11),否则不可移植。
  • 这不是您链接的问题的重复项吗?你问的是同样的问题......
  • 我知道这不是你问的,我只是想知道你为什么不提供SHARED 而不是STATIC 库?这将解决链接期间的所有静态库依赖关系,而无需您做任何额外的工作。

标签: c++ cmake


【解决方案1】:

如果您一定要创建一个静态库,那么您在原始帖子中链接的解决方案可能是最好的 (CMake: include library dependencies in a static library)。使用 ar 或 library 工具来组合静态库似乎是唯一的方法。这是 StackOverflow 上一个非常受欢迎的问题,所有答案似乎都归结为这个问题。

但是,如果可以的话,目前最简单的解决方案是创建一个共享库并将静态库链接到其中(如 cmets 中的 jszpilewski 所述)。是的,这确实意味着为运行时分发共享库。这是否实用取决于您的项目。

【讨论】:

  • 所以我将负责分发共享库?例如,以某种方式包括 GRPC 和 Protobuf?还是我只是说“您需要安装这些”?
  • 假设您有 GRPC 和 Protobuf 的静态库,通过将它们链接到共享库(例如 Windows 上的 dll),它们被捆绑到 dll 中。无需单独分发。
  • 在 Windows 上,您需要分发共享库(.dll)和链接库(.lib)。消费者将链接到 .lib 和 .dll 将需要放置在与其可执行文件相同的目录中(通常)。在 OSX(我认为)上,消费者可以直接链接到共享库(.dylib)。同样,.dylib 需要位于可执行目录中。但是,静态库和共享库之间的类公开方式存在一些很大的功能差异。 stackoverflow.com/questions/6840576/…
【解决方案2】:

子句add_executable 将始终尝试生成可供操作系统使用的二进制文件,因此它不适合生成静态库。

您可以以此为灵感,甚至可以从 Google Test 单元测试框架中借用一些代码。它使用 CMake 将 UT 框架生成为可配置的静态或动态库,使用的最终语法如下:

cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)

但它在内部定义了cxx_library 和其他一些函数。所以你应该看看他们的 CMake 包含文件internal_utils.cmake。 Google Test 附带 BSD 软件许可证。

【讨论】:

  • 我认为 add_executable 只是为了测试链接。之前对 add_library 的调用是生成库的实际调用。如果我错了,请纠正我。
  • add_executable without IMPORT 创建一个可执行文件,在这种情况下它只是一个测试客户端。提供静态库依赖项的原始问题仍然存在,它们必须显式添加到库项目中,或者在这种情况下创建动态库可能是更好的解决方案。
  • @avariant 没错。 Playground 只是一个测试程序
【解决方案3】:

如上所述here:
在编译器标志中添加-c 选项并像这样使用它:

 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -c")

如果缺少某些项目,您必须查看链接步骤;可能是一个库,或者一个目标文件。

使用 CMake 生成的 Makefile 调试意外构建失败的第一步是运行:

❯ make VERBOSE=1

这将使您深入了解 CMake 在幕后所做的事情。

参考:Symbol(s) not found for architecture x86_64 - Cmake - Mac sierra

【讨论】:

    【解决方案4】:

    将所有依赖项打包到您的库中一开始可能很诱人,因为它应该为您的用户带来最高的兼容性。但是,通常这不是一个好主意,因为您的库的大小也会很快增长。你还需要确保有一个合适的静态库,也就是说,你需要塞进每个 C 或 C++ 安装附带的所有典型系统库。否则可能会出现不兼容的问题。

    如果您编译静态库,您还应该使用-fPIC 以允许您的库在其他共享库或二进制文件中使用:

    set_target_properties(ava_engine_client PROPERTIES POSITION_INDEPENDENT_CODE on)
    

    【讨论】:

      猜你喜欢
      • 2012-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多