Vinculación de bibliotecas estáticas a otras bibliotecas estáticas

138

Tengo un pequeño fragmento de código que depende de muchas bibliotecas estáticas (a_1-a_n). Me gustaría empaquetar ese código en una biblioteca estática y ponerlo a disposición de otras personas.

Mi biblioteca estática, llamémosla X, se compila bien.

Creé un programa de muestra simple que usa una función de X, pero cuando trato de vincularlo a X, obtengo muchos errores sobre símbolos faltantes de las bibliotecas a_1 - a_n.

¿Hay alguna manera de que pueda crear una nueva biblioteca estática, Y que contenga X y toda la funcionalidad que necesita X (bits seleccionados de a_1 - a_n), para poder distribuir solo Y para que las personas vinculen sus programas?


ACTUALIZAR:

He analizado simplemente deshacer todo con ar y crear una mega lib, sin embargo, eso termina incluyendo muchos símbolos que no son necesarios (todos los archivos .o son de aproximadamente 700 MB, sin embargo, un ejecutable vinculado estáticamente es 7 MEGABYTE). ¿Hay una buena manera de incluir solo lo que realmente se necesita?


Esto se ve muy relacionado con ¿Cómo combinar varias bibliotecas C / C ++ en una? .

Jason Sundram
fuente

Respuestas:

76

Las bibliotecas estáticas no se vinculan con otras bibliotecas estáticas. La única forma de hacerlo es utilizar su herramienta de biblioteca / archivador (por ejemplo, ar en Linux) para crear una nueva biblioteca estática nueva concatenando las múltiples bibliotecas.

Editar: en respuesta a su actualización, la única forma en que sé seleccionar solo los símbolos necesarios es crear manualmente la biblioteca a partir del subconjunto de los archivos .o que los contienen. Esto es difícil, lento y propenso a errores. No conozco ninguna herramienta para ayudar a hacer esto (por no decir que no existen), pero sería un proyecto bastante interesante para producir uno.


fuente
Hola Neil, he actualizado la pregunta: ¿conoces alguna forma de incluir solo los archivos .o que son necesarios?
Jason Sundram
Actualización: si te encuentras con ganas de hacer esto, da un paso atrás y busca otra cosa (a menos que, como señala John Knoeller, estés usando Visual Studio). Dediqué mucho tiempo a este enfoque y no obtuve nada útil.
Jason Sundram
La herramienta GNU ld proporciona una opción -rcon la que la salida se puede utilizar como entrada para ld. Si vincula una vez, debe obtener un reubicable, puede sustituir sus bibliotecas. (lo probé thogh).
Harper
49

Si está utilizando Visual Studio, sí, puede hacerlo.

La herramienta de creación de bibliotecas que viene con Visual Studio le permite unir bibliotecas en la línea de comandos. Sin embargo, no conozco ninguna forma de hacer esto en el editor visual.

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib
John Knoeller
fuente
55
En VS2008, en las propiedades del proyecto de compositelib en Bibliotecario / General, si marca [x] Dependencias de la biblioteca de enlaces, lo hará por usted si lib1 y lib2 son dependencias de compositelib. Parece un poco defectuoso, establecería la casilla de verificación por separado en cada configuración de compilación, no una vez en 'Todas las configuraciones'.
Spike0xff
2
IDE VS2015: ¿no utiliza "Dependencias adicionales" en Bibliotecario / General para obtener bibliotecas adicionales vinculadas directamente a la biblioteca que está construyendo su proyecto?
davidbak
@davidbak Estoy tratando de resolver esto en los últimos días y aparentemente esta opción es obsoleta y no hace nada.
Montaldo
20

En Linux o MingW, con la cadena de herramientas GNU:

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

Si no elimina liba.ay libb.apuede hacer un "archivo delgado":

ar crsT libab.a liba.a libb.a

En Windows, con la cadena de herramientas MSVC:

lib.exe /OUT:libab.lib liba.lib libb.lib
Estrella brillante
fuente
Es bueno saberlo, pero en realidad no resuelve el problema del OP, ya que incluye todo, desde libb.a en la biblioteca conjunta, que puede ser muy grande si solo necesita unos pocos módulos de libb.
Elmar Zander
1
@ElmarZander Pero puede usar ar crsT, que hace algo como "simular" en lugar de copiar, entonces solo necesita enviar una copia de los datos.
Star Brilliant
Los archivos delgados se usan notablemente en el kernel de Linux v4.19: unix.stackexchange.com/questions/5518/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
10

Una biblioteca estática es solo un archivo de archivos de .oobjetos. Extraerlos con ar(suponiendo Unix) y empacarlos de nuevo en una gran biblioteca.

Nikolai Fetissov
fuente
6

Como alternativa a las Link Library Dependenciespropiedades del proyecto, hay otra forma de vincular bibliotecas en Visual Studio.

  1. Abra el proyecto de la biblioteca (X) que desea combinar con otras bibliotecas.
  2. Agregue las otras bibliotecas que desea combinar con X (clic derecho Add Existing Item...).
  3. Vaya a sus propiedades y asegúrese de que Item TypeseaLibrary

Esto incluirá las otras bibliotecas en X como si ejecutaras

lib /out:X.lib X.lib other1.lib other2.lib
evpo
fuente
Hola, estoy usando Intel IPP y construí mis propias funciones que quiero que se incluyan en una biblioteca (estática). Sin embargo, cuando creo la biblioteca y luego envío el proyecto a otra computadora en la que quiero poder compilar el proyecto solo usando la biblioteca que creé, recibo un error diciendo que se requiere un archivo .h de la Biblioteca Intel IPP. ¿Alguna idea?
Royi
El archivo lib generalmente no es suficiente. Si desea usarlo, también deberá incluir archivos de encabezado. Pero está fuera de tema aquí ya que este hilo habla sobre la vinculación y su problema está en la etapa de compilación.
evpo
Okay. Para ayudarte necesitaré más información. Mire el resultado de la compilación o el registro y vea qué se queja en el archivo .h que falta. Creo que es cl.exe. Si ese es el caso, le dará el nombre del archivo compilado .cpp / .cc / .c que usa el encabezado. ¿Cuál es el nombre de ese archivo .cpp y a qué proyecto pertenece?
evpo
Estoy muy confundido. Antes de leer esto, parecía que nunca había funcionado (así es como mis proyectos ya están configurados). Pero ahora que he leído esto y reinicio mis proyectos, aparentemente está funcionando. ¡Imagínate! :(
Mordachai
1
@Mordachai este haiku a continuación describe su experiencia perfectamente: ayer funcionó hoy no funciona Windows es así
evpo
5

Tenga en cuenta antes de leer el resto: el script de shell que se muestra aquí ciertamente no es seguro de usar y está bien probado. ¡Úselo bajo su propio riesgo!

Escribí un script bash para lograr esa tarea. Suponga que su biblioteca es lib1 y de la que necesita incluir algunos símbolos es lib2. El script ahora se ejecuta en un bucle, donde primero verifica qué símbolos indefinidos de lib1 se pueden encontrar en lib2. Luego extrae los archivos de objetos correspondientes de lib2 con ar, los renombra un poco y los coloca en lib1. Ahora puede haber más símbolos faltantes, porque las cosas que incluiste de lib2 necesitan otras cosas de lib2, que aún no hemos incluido, por lo que el ciclo debe ejecutarse nuevamente. Si después de algunas pasadas del bucle ya no hay cambios, es decir, no se agregaron archivos de objetos de lib2 a lib1, el bucle puede detenerse.

Tenga en cuenta que los símbolos incluidos todavía se informan como indefinidos nm, por lo que estoy haciendo un seguimiento de los archivos de objetos, que se agregaron a lib1, ellos mismos, para determinar si el ciclo se puede detener.

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

Llamé a ese script libcomp, así que puedes llamarlo entonces, por ejemplo

./libcomp libmylib.a libwhatever.a

donde libwhatever es donde desea incluir símbolos. Sin embargo, creo que es más seguro copiar primero todo en un directorio separado. No confiaría tanto en mi script (sin embargo, funcionó para mí; podría incluir libgsl.a en mi biblioteca de números con eso y omitir ese cambio de compilador -lgsl).

Elmar Zander
fuente