Связывание статических библиотек с другими статическими библиотеками

138

У меня есть небольшой фрагмент кода, который зависит от многих статических библиотек (a_1-a_n). Я хотел бы упаковать этот код в статическую библиотеку и сделать его доступным другим людям.

Моя статическая библиотека, назовем ее X, компилируется нормально.

Я создал простой пример программы, которая использует функцию из X, но когда я пытаюсь связать ее с X, я получаю много ошибок о пропущенных символах из библиотек a_1 - a_n.

Есть ли способ, которым я могу создать новую статическую библиотеку, Y, которая содержит X и все функциональные возможности, необходимые для X (выбранные биты от a_1 - a_n), чтобы я мог распространять только Y, чтобы люди могли связать их программы?


ОБНОВИТЬ:

Я смотрел на простой дамп всего с помощью ar и создание одной мегабиблиотеки, однако, в результате получается много ненужных символов (все файлы .o имеют размер около 700 МБ, однако статически связанный исполняемый файл равен 7). MB). Есть ли хороший способ включить только то, что действительно нужно?


Это тесно связано с тем, как объединить несколько библиотек C / C ++ в одну? ,

Джейсон Сундрам
источник

Ответы:

76

Статические библиотеки не связаны с другими статическими библиотеками. Единственный способ сделать это - использовать инструмент библиотекаря / архиватора (например, ar в Linux), чтобы создать единую новую статическую библиотеку путем объединения нескольких библиотек.

Редактировать: в ответ на ваше обновление единственный известный мне способ выбрать только необходимые символы - это вручную создать библиотеку из подмножества файлов .o, которые их содержат. Это сложно, отнимает много времени и подвержено ошибкам. Я не знаю каких-либо инструментов, которые могли бы помочь сделать это (не говоря уже о том, что они не существуют), но это сделало бы довольно интересный проект по его созданию.


источник
Привет, Нил, я обновил вопрос - знаешь ли ты какой-нибудь способ включить только необходимые файлы .o?
Джейсон Сундрам
Обновление - если вы обнаружите, что хотите сделать это, сделайте шаг назад и займитесь чем-нибудь другим (если, как указывает Джон Кнеллер, вы не используете Visual Studio). Я потратил много времени на этот подход и не получил ничего полезного.
Джейсон Сундрам,
Инструмент GNU ld предоставляет опцию -r, позволяющую использовать вывод в качестве ввода для ld. Если вы сделаете ссылку после того, как получите перемещаемую библиотеку, вы можете заменить ваши библиотеки. (пробовал хотя).
Harper
49

Если вы используете Visual Studio, то да, вы можете сделать это.

Инструмент для создания библиотек, поставляемый с Visual Studio, позволяет объединять библиотеки в командной строке. Однако я не знаю, как сделать это в визуальном редакторе.

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib
Джон Ноллер
источник
5
В VS2008 в свойствах проекта compositelib в разделе Librarian / General, если вы отметите [x] Link Library Dependencies, он сделает это за вас, если lib1 и lib2 являются зависимостями compositelib. Кажется, немного глючит, я бы установил флажок отдельно в каждой конфигурации сборки, а не один раз во «Все конфигурации».
Spike0xff
2
VS2015 IDE - разве вы не используете «Дополнительные зависимости» в разделе «Библиотекарь / Общие» для получения дополнительных библиотек, связанных непосредственно с библиотекой, которую создает ваш проект?
Давидбак
@davidbak Я пытаюсь понять это в последние пару дней, и, по-видимому, эта опция устарела и ничего не делает?
Монтальдо
20

В 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 в совместную библиотеку, которая может стать очень большой, если вам нужно всего несколько модулей из libb.
Эльмар Зандер
1
@ElmarZander Но вы можете использовать ar crsT, который делает что-то вроде "символической ссылки" вместо копирования, тогда вам нужно только отправить одну копию данных.
Star Brilliant
Тонкие архивы особенно используются в ядре Linux v4.19: unix.stackexchange.com/questions/5518/…
Ciro Santilli 郝海东 冠状 病 六四 事件
10

Статическая библиотека - это просто архив .oобъектных файлов. Извлеките их с помощью ar(в предположении Unix) и упакуйте обратно в одну большую библиотеку.

Николай Фетиссов
источник
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
EVPO
источник
Привет, я использую Intel IPP, и я создал свои собственные функции, которые я хочу, чтобы все они были объединены в одну (статическую) библиотеку. Тем не менее, когда я создаю библиотеку, а затем отправляю проект на другой компьютер, на котором я хочу компилировать проект только с использованием созданной мной библиотеки, я получаю сообщение об ошибке, в котором говорится, что требуется файл .h библиотеки Intel IPP. Любая идея?
Royi
lib обычно бывает недостаточно. Если вы хотите его использовать, вам также нужно будет включить файлы заголовков. Но здесь это не по теме, так как в этой ветке говорится о компоновке, а ваша проблема находится на стадии компиляции.
evpo
хорошо. Чтобы помочь вам, мне понадобится дополнительная информация. Посмотрите выходные данные сборки или журнал и посмотрите, что жалуется на отсутствующий файл .h. Думаю, это cl.exe. Если это так, он даст вам имя скомпилированного файла .cpp / .cc / .c, который использует заголовок. Как называется этот файл .cpp и какому проекту он принадлежит?
evpo
Я так растерялся. До того, как я это прочитал, казалось, что это никогда не работало (так мои проекты уже настроены). Но теперь, когда я прочитал это и перезагрузил свои проекты, это, очевидно, работает. Пойди разберись! :(
Mordachai 06
1
@Mordachai Это хайку ниже описывает ваш опыт отлично: вчера это работало сегодня это не работает Windows как то
evpo
6

Обратите внимание, прежде чем читать остальное: сценарий оболочки, показанный здесь, безусловно, небезопасен для использования и хорошо протестирован. Используйте на свой риск!

Я написал скрипт bash для выполнения этой задачи. Предположим, ваша библиотека - lib1, а из которой вам нужно включить некоторые символы - lib2. Теперь скрипт выполняется в цикле, где сначала проверяется, какие неопределенные символы из lib1 можно найти в lib2. Затем он извлекает соответствующие объектные файлы из lib2 ar, немного переименовывает их и помещает в 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

где lib, откуда вы хотите включить символы. Тем не менее, я думаю, что безопаснее всего сначала скопировать все в отдельный каталог. Я бы не стал доверять своему сценарию (однако, он сработал для меня; я мог бы включить libgsl.a в мою библиотеку чисел и пропустить этот переключатель компилятора -lgsl).

Эльмар Зандер
источник
Это отлично. Для большого проекта (> 500 тыс. Символов в> 40 тыс. Объектов в исходной библиотеке, из которых мне потребовалось ~ 1000 символов), это заняло около часа, тогда как gcc может сделать практически то же самое с динамическим связыванием за несколько секунд. Есть ли какая-то фундаментальная причина, почему это не так легко сделать и раскрыть с помощью инструментов компилятора?
ZachB