Łączenie bibliotek statycznych z innymi bibliotekami statycznymi

138

Mam mały fragment kodu, który zależy od wielu bibliotek statycznych (a_1-a_n). Chciałbym spakować ten kod do biblioteki statycznej i udostępnić go innym osobom.

Moja biblioteka statyczna, nazwijmy ją X, kompiluje się dobrze.

Stworzyłem prosty przykładowy program, który używa funkcji z X, ale kiedy próbuję połączyć go z X, otrzymuję wiele błędów dotyczących brakujących symboli z bibliotek a_1 - a_n.

Czy jest sposób, żebym mógł stworzyć nową bibliotekę statyczną, Y zawierającą X i całą funkcjonalność potrzebną X'owi (wybrane bity z a_1 - a_n), abym mógł rozprowadzać tylko Y, aby ludzie mogli łączyć swoje programy?


AKTUALIZACJA:

Przyjrzałem się po prostu zrzuceniu wszystkiego za pomocą ar i zrobieniu jednej mega-lib, która kończy się tym, że zawiera wiele symboli, które nie są potrzebne (wszystkie pliki .o mają około 700 MB, jednak statycznie linkowany plik wykonywalny ma 7 MB). Czy istnieje dobry sposób na uwzględnienie tylko tego, co jest rzeczywiście potrzebne?


Wygląda to ściśle związane z Jak połączyć kilka bibliotek C / C ++ w jedną? .

Jason Sundram
źródło

Odpowiedzi:

76

Biblioteki statyczne nie są łączone z innymi bibliotekami statycznymi. Jedynym sposobem na to jest użycie narzędzia bibliotekarza / archiwizatora (na przykład ar w systemie Linux) do utworzenia pojedynczej nowej biblioteki statycznej poprzez konkatenację wielu bibliotek.

Edycja: w odpowiedzi na Twoją aktualizację jedynym sposobem, w jaki znam, aby wybrać tylko wymagane symbole, jest ręczne utworzenie biblioteki z podzbioru plików .o, które je zawierają. Jest to trudne, czasochłonne i podatne na błędy. Nie znam żadnych narzędzi, które by w tym pomogły (żeby nie powiedzieć, że nie istnieją), ale stworzenie takiego projektu byłoby całkiem interesujące.


źródło
Cześć Neil, zaktualizowałem pytanie - czy znasz jakiś sposób na włączenie tylko niezbędnych plików .o?
Jason Sundram
Aktualizacja - jeśli okaże się, że chcesz to zrobić, cofnij się o krok i wykonaj coś innego (chyba że, jak wskazuje John Knoeller, używasz programu Visual Studio). Poświęciłem dużo czasu temu podejściu i nie znalazłem nic użytecznego.
Jason Sundram,
Narzędzie GNU ld zapewnia opcję, -rdzięki której dane wyjściowe mogą być użyte jako dane wejściowe dla ld. Jeśli połączysz się raz, powinieneś otrzymać relokowany plik, który może być zastępcą twoich bibliotek. (próbowałem tego thogh).
harper
49

Jeśli używasz programu Visual Studio, to tak, możesz to zrobić.

Narzędzie do tworzenia bibliotek, które jest dostarczane z programem Visual Studio, umożliwia łączenie bibliotek razem w wierszu polecenia. Nie znam jednak żadnego sposobu, aby to zrobić w edytorze wizualnym.

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib
John Knoeller
źródło
5
W VS2008, we właściwościach projektu compositelib w Librarian / General, jeśli zaznaczysz [x] Link Library Dependencies, zrobi to za ciebie, jeśli lib1 i lib2 są zależnościami compositelib. Wydaje się, że to trochę błędne, ustawiłbym pole wyboru osobno w każdej konfiguracji kompilacji, a nie raz w „Wszystkich konfiguracjach”.
Spike0xff
2
VS2015 IDE - czy nie używasz opcji „Additional Dependencies” w sekcji Librarian / General, aby uzyskać dodatkowe biblioteki połączone bezpośrednio z biblioteką, którą tworzy Twój projekt?
davidbak
@davidbak Próbuję to rozgryźć w ciągu ostatnich kilku dni i najwyraźniej ta opcja jest przestarzała i nic nie robi?
Montaldo
20

W systemie Linux lub MingW z zestawem narzędzi GNU:

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

Jeśli nie usuniesz liba.ai libb.a, możesz utworzyć „cienkie archiwum”:

ar crsT libab.a liba.a libb.a

W systemie Windows z łańcuchem narzędzi MSVC:

lib.exe /OUT:libab.lib liba.lib libb.lib
Gwiazda genialna
źródło
Dobrze wiedzieć, ale tak naprawdę nie rozwiązuje problemu OP, ponieważ dołączasz wszystko, od libb.a do wspólnej biblioteki, która może stać się bardzo duża, jeśli potrzebujesz tylko kilku modułów z libb.
Elmar Zander
1
@ElmarZander Ale możesz użyć ar crsT, który robi coś w rodzaju „dowiązania symbolicznego” zamiast kopiowania, wtedy wystarczy wysłać tylko jedną kopię danych.
Gwiazda genialna
Cienkie archiwa są szczególnie używane w jądrze Linux w wersji 4.19: unix.stackexchange.com/questions/5518/ ...
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
10

Biblioteka statyczna to po prostu archiwum .oplików obiektowych. Wypakuj je ar(zakładając, że Unix) i zapakuj je z powrotem do jednej dużej biblioteki.

Nikolai Fetissov
źródło
6

Alternatywnie do Link Library Dependencieswłaściwości projektu istnieje inny sposób łączenia bibliotek w programie Visual Studio.

  1. Otwórz projekt biblioteki (X), którą chcesz połączyć z innymi bibliotekami.
  2. Dodaj inne biblioteki, które chcesz połączyć z X (prawy przycisk myszy Add Existing Item...).
  3. Przejdź do ich właściwości i upewnij się, że Item TypetakLibrary

Spowoduje to włączenie innych bibliotek w X, tak jakbyś biegał

lib /out:X.lib X.lib other1.lib other2.lib
evpo
źródło
Cześć, używam Intel IPP i zbudowałem własne funkcje, które chcę, aby wszystko było opakowane w jedną (statyczną) bibliotekę. Jednak kiedy tworzę bibliotekę, a następnie wysyłam projekt na inny komputer, na którym chcę mieć możliwość skompilowania projektu tylko przy użyciu utworzonej biblioteki, pojawia się błąd informujący, że wymagany jest plik .h biblioteki Intel IPP. Dowolny pomysł?
Royi
lib zwykle nie wystarcza. Jeśli chcesz go użyć, musisz również dołączyć pliki nagłówkowe. Ale to jest poza tematem, ponieważ ten wątek mówi o linkowaniu, a twój problem jest na etapie kompilacji.
evpo
dobrze. Aby ci pomóc, potrzebuję więcej informacji. Spójrz na wynik kompilacji lub dziennik i zobacz, co narzeka na brakujący plik h. Myślę, że to cl.exe. W takim przypadku poda nazwę skompilowanego pliku .cpp / .cc / .c, który używa nagłówka. Jaka jest nazwa tego pliku .cpp i do jakiego projektu należy?
evpo
Jestem taki skołowany. Zanim to przeczytałem, wydawało się, że nigdy nie działało (tak są już skonfigurowane moje projekty). Ale teraz, gdy to przeczytałem i zresetowałem moje projekty, najwyraźniej działa. Domyśl! :(
Mordachai
1
@Mordachai to haiku poniżej doskonale opisuje Twoje doświadczenie: Wczoraj zadziałało Dzisiaj nie działa Windows jest taki
evpo
5

Uwaga, zanim przeczytasz resztę: pokazany tutaj skrypt powłoki z pewnością nie jest bezpieczny w użyciu i dobrze przetestowany. Używaj na własne ryzyko!

Napisałem skrypt basha, aby wykonać to zadanie. Załóżmy, że twoja biblioteka to lib1, a ta, z której musisz dołączyć niektóre symbole, to lib2. Skrypt działa teraz w pętli, gdzie najpierw sprawdza, które niezdefiniowane symbole z lib1 można znaleźć w lib2. Następnie wyodrębnia odpowiednie pliki obiektowe z lib2 za pomocą ar, zmienia nieco ich nazwy i umieszcza je w lib1. Teraz może być więcej brakujących symboli, ponieważ rzeczy, które dołączyłeś z lib2, wymagają innych rzeczy z lib2, których jeszcze nie uwzględniliśmy, więc pętla musi zostać uruchomiona ponownie. Jeśli po kilku przebiegach pętli nie ma już żadnych zmian, tj. Żadne pliki obiektowe z lib2 nie zostały dodane do lib1, pętla może się zatrzymać.

Zauważ, że dołączone symbole są nadal zgłaszane jako niezdefiniowane przez nm, więc śledzę same pliki obiektowe, które zostały dodane do lib1, aby określić, czy pętla może zostać zatrzymana.

#! /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

Nazwałem ten skrypt libcomp, więc możesz go wtedy nazwać np

./libcomp libmylib.a libwhatever.a

gdzie libcokolwiek jest skąd chcesz dołączyć symbole. Uważam jednak, że najbezpieczniej jest najpierw skopiować wszystko do oddzielnego katalogu. Nie ufałbym tak bardzo mojemu skryptowi (jednak zadziałał dla mnie; mógłbym dołączyć libgsl.a do mojej biblioteki numerycznej i pominąć ten przełącznik kompilatora -lgsl).

Elmar Zander
źródło