ChatGPT解决这个技术问题 Extra ChatGPT

将静态库链接到其他静态库

我有一小段代码依赖于许多静态库(a_1-a_n)。我想将该代码打包在一个静态库中,并使其可供其他人使用。

我的静态库,我们称之为 X,编译得很好。

我创建了一个使用 X 中的函数的简单示例程序,但是当我尝试将其链接到 X 时,我收到很多关于缺少库 a_1 - a_n 中的符号的错误。

有没有一种方法可以创建一个新的静态库 Y,其中包含 X 和 X 所需的所有功能(从 a_1 - a_n 中选择的位),以便我可以只分发 Y 以供人们将他们的程序链接到?

更新:

我看过只是用 ar 转储所有内容并制作一个 mega-lib,但是,最终包含许多不需要的符号(所有 .o 文件大约 700 MB,但是,静态链接的可执行文件是 7兆)。有没有一种好方法可以只包含实际需要的内容?

这看起来与 How to combine several C/C++ libraries into one? 密切相关。


佚名

静态库不与其他静态库链接。这样做的唯一方法是使用您的图书管理员/归档工具(例如 Linux 上的 ar)通过连接多个库来创建一个新的静态库。

编辑:为了响应您的更新,我知道仅选择所需符号的唯一方法是从包含它们的 .o 文件的子集中手动创建库。这是困难、耗时且容易出错的。我不知道有任何工具可以帮助做到这一点(并不是说它们不存在),但是制作一个会是一个非常有趣的项目。


嗨,尼尔,我已经更新了这个问题——你知道有什么方法可以只包含必要的 .o 文件吗?
更新——如果您发现自己想要这样做,请退后一步,追求其他东西(除非,正如 John Knoeller 所指出的,您使用的是 Visual Studio)。我在这种方法上投入了很多时间,但没有得到任何有用的东西。
GNU ld 工具提供了一个选项 -r,其输出可用作 ld 的输入。如果你链接一次,你应该得到一个可重定位的,可以替代你的库。 (确实尝试过)。
J
John Knoeller

如果您使用的是 Visual Studio,那么可以,您可以这样做。

Visual Studio 附带的库构建器工具允许您在命令行上将库连接在一起。不过,我不知道在可视化编辑器中执行此操作的任何方法。

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib

在VS2008中,在Librarian/General下compositelib的项目属性中,如果你勾选[x] Link Library Dependencies,如果lib1和lib2是compositelib的依赖,它会为你做这个。看起来有点错误,我会在每个构建配置中单独设置复选框,而不是在“所有配置”中设置一次。
VS2015 IDE - 你不使用 Librarian/General 下的“Additional Dependencies”来让其他库直接链接到你的项目正在构建的库中吗?
@davidbak我在过去几天试图解决这个问题,显然这个选项已经过时并且什么都不做?
S
Star Brilliant

在 Linux 或 MingW 上,使用 GNU 工具链:

ar -M <<EOM
    CREATE libab.a
    ADDLIB liba.a
    ADDLIB libb.a
    SAVE
    END
EOM
ranlib libab.a

如果您不删除 liba.alibb.a,您可以制作“精简存档”:

ar crsT libab.a liba.a libb.a

在 Windows 上,使用 MSVC 工具链:

lib.exe /OUT:libab.lib liba.lib libb.lib

很高兴知道,但并没有真正解决 OP 的问题,因为您将 libb.a 中的所有内容都包含到联合库中,如果您只需要来自 libb 的几个模块,它可能会变得非常大。
@ElmarZander 但是您可以使用ar crsT,它执行“符号链接”之类的操作而不是复制,然后您只需要发送一份数据副本。
精简存档特别用于 Linux 内核 v4.19:unix.stackexchange.com/questions/5518/…
N
Nikolai Fetissov

静态库只是 .o 对象文件的存档。用 ar 提取它们(假设是 Unix)并将它们打包回一个大库中。


E
Elmar Zander

在阅读其余内容之前请注意:此处显示的 shell 脚本使用起来肯定不安全,并且经过了很好的测试。使用风险自负!

我编写了一个 bash 脚本来完成该任务。假设您的库是 lib1,而您需要从中包含一些符号的库是 lib2。该脚本现在在一个循环中运行,它首先检查 lib1 中哪些未定义符号可以在 lib2 中找到。然后它使用 ar 从 lib2 中提取相应的目标文件,稍微重命名它们,然后将它们放入 lib1。现在可能缺少更多符号,因为您从 lib2 中包含的内容需要来自 lib2 的其他内容,而我们尚未包含这些内容,因此循环需要再次运行。如果在循环经过一些循环后不再有任何变化,即没有来自 lib2 的目标文件添加到 lib1,则循环可以停止。

请注意,包含的符号仍报告为 nm 未定义,因此我正在跟踪添加到 lib1 的目标文件本身,以确定是否可以停止循环。

#! /bin/bash

lib1="$1"
lib2="$2"

if [ ! -e $lib1.backup ]; then
    echo backing up
    cp $lib1 $lib1.backup
fi

remove_later=""

new_tmp_file() {
    file=$(mktemp)
    remove_later="$remove_later $file"
    eval $1=$file
}
remove_tmp_files() {
    rm $remove_later
}
trap remove_tmp_files EXIT

find_symbols() {
    nm $1 $2 | cut -c20- | sort | uniq 
}

new_tmp_file lib2symbols
new_tmp_file currsymbols

nm $lib2 -s --defined-only > $lib2symbols

prefix="xyz_import_"
pass=0
while true; do
    ((pass++))
    echo "Starting pass #$pass"
    curr=$lib1
    find_symbols $curr "--undefined-only" > $currsymbols
    changed=0
    for sym in $(cat $currsymbols); do
        for obj in $(egrep "^$sym in .*\.o" $lib2symbols | cut -d" " -f3); do
            echo "  Found $sym in $obj."
            if [ -e "$prefix$obj" ]; then continue; fi
            echo "    -> Adding $obj to $lib1"
            ar x $lib2 $obj
            mv $obj "$prefix$obj"
            ar -r -s $lib1 "$prefix$obj"
            remove_later="$remove_later $prefix$obj"
            ((changed=changed+1))
        done
    done
    echo "Found $changed changes in pass #$pass"

    if [[ $changed == 0 ]]; then break; fi
done

我将该脚本命名为 libcomp,因此您可以调用它,例如

./libcomp libmylib.a libwhatever.a

libwhatever 是您要从中包含符号的位置。但是,我认为首先将所有内容复制到单独的目录中是最安全的。我不会那么信任我的脚本(但是,它对我有用;我可以将 libgsl.a 包含到我的数字库中,并省略 -lgsl 编译器开关)。


这是极好的。对于一个大型项目(原始库中 >40k 个对象中 >500k 个符号,其中我需要大约 1000 个符号),这需要将近一个小时,而 gcc 可以在几秒钟内完成与动态链接基本相同的事情。是否有一些根本原因导致编译器工具不容易做到这一点并暴露出来?
e
evpo

除了项目属性中的 Link Library Dependencies,还有另一种方法可以在 Visual Studio 中链接库。

打开要与其他库组合的库 (X) 的项目。添加要与 X 组合的其他库(右键单击,添加现有项目...)。转到他们的属性并确保项目类型是库

这将包括 X 中的其他库,就像您运行一样

lib /out:X.lib X.lib other1.lib other2.lib

嗨,我正在使用英特尔 IPP,并且我构建了自己的函数,我希望将它们全部打包到一个(静态)库中。然而,当我创建库然后将项目发送到我希望能够仅使用我创建的库编译项目的其他计算机时,我收到一条错误消息,提示需要英特尔 IPP 库的 .h 文件。任何想法?
lib 文件通常是不够的。如果你想使用它,你也需要包含头文件。但这不是主题,因为该线程讨论了链接,而您的问题处于编译阶段。
好的。为了帮助你,我需要更多信息。查看构建输出或日志,看看丢失的 .h 文件有什么抱怨。我认为它是 cl.exe。如果是这种情况,它将为您提供使用标头的已编译 .cpp/.cc/.c 文件的名称。该 .cpp 文件的名称是什么,它属于哪个项目?
我感到很困惑。在我阅读本文之前,它似乎从未起作用(这就是我的项目已经配置的方式)。但是现在我已经阅读了这篇文章并重置了我的项目,它显然正在工作。去搞清楚! :(
@Mordachai 下面这个俳句完美地描述了您的体验:昨天它工作了今天它不工作 Windows 就是这样