Lier des bibliothèques statiques à d'autres bibliothèques statiques

138

J'ai un petit morceau de code qui dépend de nombreuses bibliothèques statiques (a_1-a_n). J'aimerais empaqueter ce code dans une bibliothèque statique et le rendre disponible à d'autres personnes.

Ma bibliothèque statique, appelons-la X, se compile très bien.

J'ai créé un exemple de programme simple qui utilise une fonction de X, mais lorsque j'essaie de la lier à X, j'obtiens de nombreuses erreurs concernant les symboles manquants dans les bibliothèques a_1 - a_n.

Existe-t-il un moyen de créer une nouvelle bibliothèque statique, Y qui contient X et toutes les fonctionnalités nécessaires à X (bits sélectionnés de a_1 - a_n), afin que je puisse distribuer uniquement Y pour que les gens puissent lier leurs programmes?


METTRE À JOUR:

J'ai regardé simplement vider tout avec ar et créer une méga-lib, cependant, qui finit par inclure beaucoup de symboles qui ne sont pas nécessaires (tous les fichiers .o font environ 700 Mo, cependant, un exécutable lié statiquement est 7 MB). Existe-t-il une bonne façon de n'inclure que ce qui est réellement nécessaire?


Cela semble étroitement lié à Comment combiner plusieurs bibliothèques C / C ++ en une seule? .

Jason Sundram
la source

Réponses:

76

Les bibliothèques statiques ne sont pas liées à d'autres bibliothèques statiques. La seule façon de faire est d'utiliser votre outil de bibliothécaire / archiveur (par exemple ar sous Linux) pour créer une seule nouvelle bibliothèque statique en concaténant les multiples bibliothèques.

Modifier: En réponse à votre mise à jour, le seul moyen que je connaisse pour sélectionner uniquement les symboles requis est de créer manuellement la bibliothèque à partir du sous-ensemble des fichiers .o qui les contiennent. C'est difficile, chronophage et sujet aux erreurs. Je ne connais aucun outil pour aider à faire cela (pour ne pas dire qu'ils n'existent pas), mais cela ferait un projet assez intéressant d'en produire un.


la source
Salut Neil, j'ai mis à jour la question - connaissez-vous un moyen d'inclure uniquement les fichiers .o nécessaires?
Jason Sundram
Mise à jour - Si vous souhaitez faire cela, prenez du recul et poursuivez autre chose (à moins que, comme le souligne John Knoeller, vous n'utilisiez Visual Studio). J'ai plongé beaucoup de temps dans cette approche et je n'ai rien obtenu d'utile.
Jason Sundram
L'outil GNU ld fournit une option -ravec laquelle la sortie peut être utilisée comme entrée pour ld. Si vous créez un lien une fois que vous devriez obtenir un relocalisable, vous pouvez remplacer vos bibliothèques. (Je l'ai essayé thogh).
harper
49

Si vous utilisez Visual Studio, oui, vous pouvez le faire.

L'outil de création de bibliothèque fourni avec Visual Studio vous permet de joindre des bibliothèques ensemble sur la ligne de commande. Cependant, je ne connais aucun moyen de le faire dans l'éditeur visuel.

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib
John Knoeller
la source
5
Dans VS2008, dans les propriétés du projet de compositelib sous Librarian / General, si vous cochez [x] Link Library Dependencies, il le fera pour vous si lib1 et lib2 sont des dépendances de compositelib. Cela semble légèrement bogué, je définirais la case à cocher séparément dans chaque configuration de construction, pas une seule fois dans `` Toutes les configurations ''.
Spike0xff
2
VS2015 IDE - n'utilisez-vous pas "Dépendances supplémentaires" sous Bibliothécaire / Général pour obtenir des bibliothèques supplémentaires liées directement dans la bibliothèque que votre projet est en train de construire?
davidbak
@davidbak J'essaie de comprendre cela ces derniers jours et apparemment cette option est obsolète et ne fait rien?
Montaldo
20

Sous Linux ou MingW, avec la chaîne d'outils GNU:

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

De si vous ne supprimez pas liba.aet libb.a, vous pouvez créer une "archive légère":

ar crsT libab.a liba.a libb.a

Sous Windows, avec la chaîne d'outils MSVC:

lib.exe /OUT:libab.lib liba.lib libb.lib
Étoile brillante
la source
Bon à savoir, mais ne résout pas vraiment le problème de l'OP, puisque vous incluez tout de libb.a dans la bibliothèque commune, qui peut devenir très volumineuse si vous n'avez besoin que de quelques modules de la libb.
Elmar Zander
1
@ElmarZander Mais vous pouvez utiliser ar crsT, qui fait quelque chose comme "symlinking" au lieu de copier, alors vous n'avez qu'à expédier une copie des données.
Star Brilliant
Les archives minces sont notamment utilisées sur le noyau Linux v4.19: unix.stackexchange.com/questions/5518/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
10

Une bibliothèque statique n'est qu'une archive de .ofichiers objets. Extrayez-les avec ar(en supposant Unix) et remettez-les dans une grande bibliothèque.

Nikolai Fetissov
la source
6

Vous pouvez également à Link Library Dependenciesdes propriétés du projet il y a une autre façon de bibliothèques de liens dans Visual Studio.

  1. Ouvrez le projet de la bibliothèque (X) que vous souhaitez combiner avec d'autres bibliothèques.
  2. Ajoutez les autres bibliothèques que vous souhaitez combiner avec X (clic droit, Add Existing Item...).
  3. Accédez à leurs propriétés et assurez - vous Item TypeestLibrary

Cela inclura les autres bibliothèques de X comme si vous exécutiez

lib /out:X.lib X.lib other1.lib other2.lib
evpo
la source
Bonjour, j'utilise Intel IPP et j'ai construit mes propres fonctions que je veux que toutes soient enveloppées dans une seule bibliothèque (statique). Pourtant, lorsque je crée la bibliothèque et que j'envoie le projet à un autre ordinateur sur lequel je veux être en mesure de compiler le projet uniquement en utilisant la bibliothèque que j'ai créée, j'obtiens une erreur disant qu'un fichier .h de la bibliothèque Intel IPP est requis. Une idée?
Royi
Le fichier lib ne suffit généralement pas. Si vous souhaitez l'utiliser, vous devrez également inclure des fichiers d'en-tête. Mais c'est hors sujet ici car ce fil parle de liaison et votre problème est au stade de la compilation.
evpo
D'accord. Pour vous aider, j'aurai besoin de plus d'informations. Regardez la sortie de construction ou le journal et voyez ce qui se plaint sur le fichier .h manquant. Je pense que c'est cl.exe. Si tel est le cas, il vous donnera le nom du fichier .cpp / .cc / .c compilé qui utilise l'en-tête. Quel est le nom de ce fichier .cpp et à quel projet il appartient?
evpo
Je suis tellement confus. Avant de lire ceci, cela ne semble pas avoir jamais fonctionné (c'est ainsi que mes projets sont déjà configurés). Mais maintenant que j'ai lu ceci et réinitialisé mes projets, cela fonctionne apparemment. Allez comprendre! :(
Mordachai
1
@Mordachai, ce haïku ci-dessous décrit parfaitement votre expérience: Hier, cela a fonctionné Aujourd'hui, cela ne fonctionne pas Windows est comme ça
evpo
5

Remarque avant de lire la suite: le script shell montré ici n'est certainement pas sûr à utiliser et bien testé. À utiliser à vos risques et périls!

J'ai écrit un script bash pour accomplir cette tâche. Supposons que votre bibliothèque soit lib1 et que celle dont vous ayez besoin pour inclure certains symboles soit lib2. Le script s'exécute maintenant dans une boucle, où il vérifie d'abord quels symboles non définis de lib1 peuvent être trouvés dans lib2. Il extrait ensuite les fichiers objets correspondants de la lib2 avec ar, les renomme un peu et les met dans lib1. Maintenant, il peut y avoir plus de symboles manquants, parce que les éléments que vous avez inclus de lib2 ont besoin d'autres éléments de lib2, que nous n'avons pas encore inclus, donc la boucle doit être exécutée à nouveau. Si après quelques passages de la boucle il n'y a plus de changements, c'est-à-dire qu'aucun fichier objet de la lib2 n'a été ajouté à la lib1, la boucle peut s'arrêter.

Notez que les symboles inclus sont toujours signalés comme non définis par nm, donc je garde une trace des fichiers objets, qui ont été ajoutés à lib1, eux-mêmes, afin de déterminer si la boucle peut être arrêtée.

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

J'ai nommé ce script libcomp, donc vous pouvez l'appeler alors par exemple avec

./libcomp libmylib.a libwhatever.a

où libwthing est d'où vous voulez inclure des symboles. Cependant, je pense qu'il est plus sûr de tout copier d'abord dans un répertoire séparé. Je ne ferais pas tellement confiance à mon script (cependant, cela a fonctionné pour moi; je pourrais inclure libgsl.a dans ma bibliothèque numérique avec cela et laisser de côté ce commutateur de compilateur -lgsl).

Elmar Zander
la source