【问题标题】:Linking static libraries to other static libraries将静态库链接到其他静态库
【发布时间】:2011-01-10 14:05:16
【问题描述】:

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

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

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

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


更新:

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


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

【问题讨论】:

    标签: c++ linker unix-ar .a


    【解决方案1】:

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

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

    【讨论】:

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

    如果您使用的是 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 我在过去几天试图解决这个问题,显然这个选项已经过时并且没有任何作用?
    【解决方案3】:

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

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

    如果不删除liba.a和libb.a,可以做一个“精简存档”:

    ar crsT libab.a liba.a libb.a
    

    在 Windows 上,使用 MSVC 工具链:

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

    【讨论】:

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

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

    【讨论】:

      【解决方案5】:

      在阅读其余内容之前请注意:此处显示的 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 可以在几秒钟内完成与动态链接基本相同的事情。是否有一些根本原因导致编译器工具不容易做到这一点并暴露出来?
      【解决方案6】:

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

      1. 打开要与其他库合并的库 (X) 的项目。
      2. 添加您想与 X 结合的其他库(右键单击,Add Existing Item...)。
      3. 转到他们的属性并确保Item Type 是Library

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

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

      【讨论】:

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