¿Es posible hacer que CMake cree una versión estática y compartida de la misma biblioteca?

141

La misma fuente, todo eso, solo quiere una versión estática y compartida. ¿Fácil de hacer?

gct
fuente

Respuestas:

123

Sí, es moderadamente fácil. Simplemente use dos comandos "add_library":

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)

Incluso si tiene muchos archivos fuente, colocaría la lista de fuentes en una variable cmake, por lo que aún es fácil de hacer.

En Windows, probablemente debería darle a cada biblioteca un nombre diferente, ya que hay un archivo ".lib" para compartir y estático. Pero en Linux y Mac, incluso puede dar a ambas bibliotecas el mismo nombre (por ejemplo, libMyLib.ay libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Pero no recomiendo dar el mismo nombre a las versiones estáticas y dinámicas de la biblioteca. Prefiero usar nombres diferentes porque eso hace que sea más fácil elegir el enlace estático frente al dinámico en la línea de compilación para las herramientas que enlazan con la biblioteca. Por lo general, elijo nombres como libMyLib.so(compartido) y libMyLib_static.a(estático). (Esos serían los nombres en Linux).

Christopher Bruns
fuente
Esperaba que tuvieran el mismo nombre, pero bueno. Otra pregunta: ¿puede decirle a CMake que vincule las bibliotecas estáticas a la biblioteca compartida cuando sea posible?
gct
Más sobre "mismo nombre": si está en Windows y desea el mismo nombre para ambas bibliotecas y no necesita el archivo .lib compartido, es posible crear un .lib estático y un .dll compartido. Pero necesita ese archivo .lib compartido si está utilizando su biblioteca para el tiempo de compilación normal.
Christopher Bruns el
1
No estoy seguro de entender su pregunta sobre la vinculación de bibliotecas estáticas en la biblioteca compartida.
Christopher Bruns
55
Tenga en cuenta que esta ya no es la forma sugerida de hacerlo. Para proyectos de tamaño no trivial (los que toman minutos, no segundos para compilar), evitar duplicar el tiempo de compilación es maravilloso. Consulte la respuesta de user465139 a continuación para ver el uso de la Biblioteca de objetos o los documentos: cmake.org/cmake/help/v3.8/command/…
KymikoLoco
3
@KymikoLoco: el enfoque de la Biblioteca de objetos reduce el tiempo de compilación a la mitad, pero requiere que las bibliotecas estáticas se construyan como Código independiente de la posición (es decir, con -fPIC), que agrega una pequeña cantidad de tiempo de ejecución cuando se usan esas bibliotecas estáticas. Entonces, para obtener el máximo rendimiento, esta respuesta sigue siendo la mejor.
John Zwinck
95

Desde CMake versión 2.8.8, puede usar "bibliotecas de objetos" para evitar la compilación duplicada de los archivos de objetos . Usando el ejemplo de Christopher Bruns de una biblioteca con dos archivos fuente:

# list of source files
set(libsrc source1.c source2.c)

# this is the "object library" target: compiles the sources only once
add_library(objlib OBJECT ${libsrc})

# shared libraries need PIC
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)

# shared and static libraries built from the same object files
add_library(MyLib_shared SHARED $<TARGET_OBJECTS:objlib>)
add_library(MyLib_static STATIC $<TARGET_OBJECTS:objlib>)

De los documentos de CMake :

Una biblioteca de objetos compila archivos fuente pero no archiva ni vincula sus archivos de objetos en una biblioteca. En cambio, otros objetivos creados por add_library()o add_executable()pueden hacer referencia a los objetos utilizando una expresión del formulario $<TARGET_OBJECTS:objlib>como fuente, donde objlib es el nombre de la biblioteca de objetos.

En pocas palabras, el add_library(objlib OBJECT ${libsrc})comando indica a CMake que compile los archivos de origen en archivos de *.oobjetos. A *.ocontinuación, se hace referencia a esta colección de archivos como $<TARGET_OBJECT:objlib>en los dos add_library(...)comandos que invocan los comandos de creación de bibliotecas apropiados que crean las bibliotecas compartidas y estáticas a partir del mismo conjunto de archivos de objetos. Si tiene muchos archivos fuente, compilarlos *.opuede llevar bastante tiempo; con bibliotecas de objetos las compilas solo una vez.

El precio que paga es que los archivos de objeto deben construirse como código independiente de la posición porque las bibliotecas compartidas necesitan esto (a las bibliotecas estáticas no les importa). Tenga en cuenta que el código independiente de la posición puede ser menos eficiente, por lo que si busca un rendimiento máximo, optará por bibliotecas estáticas. Además, es más fácil distribuir ejecutables vinculados estáticamente.

Laryx Decidua
fuente
3
Esto funcionó de maravilla para mí: la única advertencia fue que las target_link_libraries()llamadas posteriores que dependen de su biblioteca no pueden usar la "biblioteca de objetos" para enlazar; deben dirigirse a las nuevas bibliotecas compartidas o estáticas (y pueden estar duplicadas). Pero, contrariamente a la experiencia de los primeros comentaristas, esto fue bastante útil y me permitió eliminar todos los objetivos duplicados y cortar todos mis CMakeLists.txtarchivos a la mitad.
fish2000
1
¿Necesita "escapar" del obblib al configurar las propiedades de destino? es decir, set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac
1
Quien haya rechazado esto ... ¿podría esa persona dar una explicación de lo que él considera incorrecto? Más aún porque esta es la forma recomendada de hacer lo que quiere el OP, vea los documentos de CMake.
Laryx Decidua
1
@ user465139 Quizás debería explicar por qué debería funcionar reutilizar archivos de objetos para el objetivo estático y compartido. Especialmente, el conocimiento general en SO todavía es muy confuso al respecto, los archivos antiguos / tampoco ayudan a aclararlo, por ejemplo. cmake.org/pipermail/cmake/2008-March/020315.html Se necesita una explicación sólida del status quo. PS No era yo que downvoted
mloskot
2
@gnac No puedo confirmar esto. En mi caso, set_propertysolo funcionaba cuando lo usaba objliby no cuando lo usaba ${objlib}. Entonces, ¿tal vez esta respuesta podría corregirse?
josch
22

Generalmente no hay necesidad de duplicar ADD_LIBRARYllamadas para su propósito. Solo haz uso de

$> man cmake | grep -A6 '^ *BUILD_SHARED_LIBS$' 
   BUILD_SHARED_LIBS
          Global flag to cause add_library to create shared libraries if on.

          If present and true, this will cause all libraries to be built shared unless the library was
          explicitly added as a static library.  This variable is often added to projects as an OPTION
          so  that each user of a project can decide if they want to build the project using shared or
          static libraries.

mientras construye, primero (en un directorio fuera de la fuente) con -DBUILD_SHARED_LIBS:BOOL=ON, y con OFFen el otro.

Yaroslav Halchenko
fuente
43
Esto no parece construir AMBAS versiones estáticas y compartidas, que creo que es a lo que se refiere esta pregunta.
Nick Desaulniers
0

Es posible empacar todo en el mismo aliento de compilación, como se sugirió en las respuestas anteriores, pero recomendaría no hacerlo, porque al final es un truco que funciona solo para proyectos simples. Por ejemplo, es posible que en algún momento necesite diferentes indicadores para diferentes versiones de la biblioteca (especialmente en Windows, donde los indicadores generalmente se usan para cambiar entre exportar símbolos o no). O como se mencionó anteriormente, es posible que desee colocar .libarchivos en diferentes directorios dependiendo de si corresponden a bibliotecas estáticas o compartidas. Cada uno de esos obstáculos requerirá un nuevo truco.

Puede ser obvio, pero una alternativa que no se ha mencionado anteriormente es hacer que el tipo de biblioteca sea un parámetro:

set( ${PROJECT_NAME}_LIBTYPE CACHE STRING "library type" )
set_property( CACHE ${PROJECT_NAME}_LIBTYPE PROPERTY STRINGS "SHARED;STATIC" )
add_library( ${PROJECT_NAME} ${PROJECT_NAME}_LIBTYPE ${SOURCE_FILES} )

Tener versiones compartidas y estáticas de la biblioteca en dos árboles binarios diferentes facilita el manejo de diferentes opciones de compilación. No veo ningún inconveniente serio en mantener distintos árboles de compilación, especialmente si sus compilaciones están automatizadas.

Tenga en cuenta que incluso si tiene la intención de mutualizar compilaciones utilizando una OBJECTbiblioteca intermedia (con las advertencias mencionadas anteriormente, por lo que necesita una razón convincente para hacerlo), aún podría tener bibliotecas finales en dos proyectos diferentes.

P-Gn
fuente
-2

De hecho es posible. Como @Christopher Bruns dijo en su respuesta, debe agregar dos versiones de la biblioteca:

set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})

Luego, como se describe aquí , debe especificar que ambos objetivos deben usar el mismo nombre de salida y no sobrescribir los archivos del otro:

SET_TARGET_PROPERTIES(mylib-static PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(mylib-shared PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)

De esta manera, obtendrá libmylib.a y libmylib.so (en Linux) o mylib.lib y mylib.dll (en Windows).

Alexander Amelkin
fuente
10
Esto es innecesario cuando se utilizan versiones CMake anteriores a 2.8. [0?], Ya que la propiedad se eliminó en 2009, y el comportamiento que proporciona ahora es el predeterminado. Esto puede ser útil para personas menores de 2.8, pero si todavía está usando CMake <2.7, le imploro que actualice. github.com/Kitware/CMake/commit/…
KymikoLoco