Respuestas:
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.a
y 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).
-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.
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()
oadd_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 *.o
objetos. A *.o
continuació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 *.o
puede 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.
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.txt
archivos a la mitad.
set_property
solo funcionaba cuando lo usaba objlib
y no cuando lo usaba ${objlib}
. Entonces, ¿tal vez esta respuesta podría corregirse?
Generalmente no hay necesidad de duplicar ADD_LIBRARY
llamadas 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 OFF
en el otro.
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 .lib
archivos 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 OBJECT
biblioteca 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.
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).