【问题标题】:How to make an extern method defined in a shared library but declared in other target using cmake?如何使用 cmake 在共享库中定义但在其他目标中声明的外部方法?
【发布时间】:2025-12-27 12:10:07
【问题描述】:

问题如下:您有两个文件,一个用于库,一个用于可执行文件,您要调用库中定义但在可执行文件中声明的方法:

lib.cpp:

extern void Method();


void DoStuff() {
    Method();
}

ma​​in.cpp:

#include <iostream>

void Method() {
    std::cout << "Do Stuff" << std::endl;
}

int main() {
    Method();
    return 0;
}

现在使用 cmake 我只是想告诉库 DoStuff 方法的声明在可执行目标中。

使用这个简单的 CMakeLists.txt 会导致未定义的符号链接器错误:

Undefined symbols for architecture x86_64:
  "Method()", referenced from:
     DoStuff() in lib.cpp.o

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(extern)

set(CMAKE_CXX_STANDARD 14)

add_library(myLib SHARED lib.cpp)    
add_executable(exe main.cpp)

target_link_libraries(exe myLib)

我发现的唯一方法是通过一个临时静态库并将我的共享库与那个我认为非常讨厌的静态库链接,因为我很快就会向可执行文件添加更多类我得到重复的符号(这完全有道理因为它们是重复的)

add_library(tmpLib STATIC main.cpp)
target_link_libraries(myLib tmpLib)

我已经浏览了两个晚上的互联网,现在正在寻找一种方法来做到这一点,但我仍然完全没有任何线索。

【问题讨论】:

    标签: c++ cmake c++14


    【解决方案1】:

    链接目标文件以创建库文件时发生错误。在add_library 中尝试OBJECT 关键字,而不是SHARED。这将创建目标文件,但不会链接它们。然后,您可以使用target_link_libraries 相同的方式将这些目标文件链接到您的可执行文件。由于链接器将运行一次以创建您的可执行文件,并且您的可执行文件包含Method 的定义,因此链接器不应抱怨。

    另外,extern 说明符不是必需的,因为函数默认具有外部链接。

    根据您的链接器,可能有一种晦涩的方式告诉它忽略未解析的符号,但这对于其他用户/开发人员和您未来的自己来说是非常不合常规、违反直觉、出乎意料和困惑的,因此您应该避免使用它不惜一切代价使用真实代码。详情见this问题。

    注意:您的代码在 Debian 10 x64 和 g++ 8.3.0、GNU ld 2.32.1 上运行良好。

    【讨论】:

      【解决方案2】:

      经过几个小时的搜索,我终于找到了解决方案。

      set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,dynamic_lookup")
      

      只需要!它告诉链接器通过 dynamic_lookup 为共享库找到未定义的符号。请注意,这仅适用于 macOS 链接器。对于其他 gcc 变体,应使用 -Wl,allow-shlib-undefined。有关此的更多信息SO Post

      【讨论】: