2012-07-11 15 views
38

que tienen la siguiente distribución:CMake: ¿cómo crear una sola biblioteca compartida desde todas las bibliotecas estáticas de subproyectos?

top_project 
    + subproject1 
    + subproject2 

Cada uno de subproject1 y subproject2 crea una biblioteca estática. Me gustaría vincular estas bibliotecas estáticas en una única biblioteca compartida en el nivel top_project.

La información recopilada hasta el momento es:

  • compilar usando -fPic (necesaria en todo menos en Windows) con el fin de crear un código independiente de la posición que permitirá la vinculación de las bibliotecas estáticas en una sola biblioteca compartida o descomprimir todas las bibliotecas estáticas (por ejemplo, utilizando ar) y volver a vincularlas en una biblioteca compartida (que creo que es una solución no portátil & no elegante
  • Todos los archivos de origen se deben dar explícitamente al comando add_library: por alguna razón que No puedo comprender, simplemente escribiendo add_library(${PROJECT_NAME} SHARED subproject1 subproject2) no funciona como se esperaba (esencialmente crea una biblioteca vacía & no registra las dependencias correctamente)
  • Hay una característica de biblioteca OBJECT en CMake pero no creo que su propósito sea realmente hacer lo que quiero.

¿Alguna idea?

+0

Estoy usando cmake 3.4. + Y simplemente agrego libs estáticos a lib compartido y se compilan a un solo archivo :) Probé esto en android :) –

+0

¿Alguien podría tener una pista sobre cómo hacer esto bajo MSVC? Estoy usando qmake y no cmake pero puedo manejar los pasos yo mismo, si puedo resolverlos ... –

Respuesta

27

OK, me di cuenta: esto es mucho más doloroso de lo que debería ser. Hasta hace muy poco, la gente de Kitware no entendía por qué alguien querría crear una DLL desde libs estáticos. Su argumento es que siempre debe haber archivos fuente en el directorio principal (por ejemplo, top_project en mi caso) porque es efectivamente un proyecto propio. Veo las cosas de manera diferente & Necesito romper top_project en subproyectos más pequeños que no deberían existir de forma independiente (es decir, no tiene sentido crear un proyecto completo para ellos & agréguelos usando ExternalProject_Add). Además, cuando envío mi biblioteca compartida (para usar, por ejemplo, con una interfaz nativa de Java), no deseo enviar docenas de bibliotecas compartidas porque eso equivaldría a exponer el diseño interno de mi proyecto. De todos modos, creo, creo, un caso para crear una biblioteca compartida de bibliotecas estáticas, procederé a los detalles técnicos.

En el CMakeLists.txt de subproject1 y subproject2, debe crear su destino mediante la función de biblioteca de objetos (introducido en CMake 2.8.8):

add_library(${PROJECT_NAME} OBJECT ${SRC}) 

donde SRC designa la lista de archivos de origen (nota que estos deben establecerse explícitamente en el archivo CMakeLists.txt, ya que permite a make reiniciar CMake cuando se detecta una modificación de CMakeLists.txt, por ejemploal añadir o eliminar un archivo)

En el top_project, añadir los subproyectos usando:

add_subdirectory(subproject1) 
add_subdirectory(subproject2) 

Con el fin de ver los símbolos de la biblioteca estática, utilice:

set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols") 

A continuación, puede crear la biblioteca compartida usando:

add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1> 
            $<TARGET_OBJECTS:subproject2>) 

He encontrado que cualquier libr "normal" aria (es decir no objeto) debe agregarse en un comando add_library por separado; de lo contrario, simplemente se ignorará.

para ejecutables, puede utilizar:

add_executable(name_of_executable $<TARGET_OBJECTS:subproject1> 
        $<TARGET_OBJECTS:subproject2>) 
set(LINK_FLAGS ${LINK_FLAGS} "-Wl,-whole-archive") 
target_link_libraries(name_of_executable ${PROJECT_NAME} 

repito que esto sólo funciona partir de la versión 2.8.8 de CMake. De la misma manera CMake maneja extremadamente bien las dependencias & es multiplataforma porque no es mucho menos doloroso que los viejos y simples Makefiles & ciertamente menos flexibles.

+0

Bah, molestamente Ubuntu 12.04 está atascado en CMake 2.8.7, ¿hay alguna alternativa para versiones anteriores? ¿Tenemos que referirnos a todos los archivos fuente al definir la biblioteca? – Ibrahim

+2

Trabajé alrededor de mis problemas mediante la compilación de mis bibliotecas estáticas con -fPIC, mi biblioteca compartida no se vinculó correctamente, pero no sé si realmente funciona, ya que no he intentado utilizarlo todavía. – Ibrahim

1

Otra forma de hacerlo es proporcionar la ruta de los archivos de código fuente y los archivos de cabecera de toda su proyectos, y construirlos juntos para producir el .so. Esta suele ser la forma recomendada, en lugar de crear las bibliotecas estáticas y luego una biblioteca compartida de las mismas.

Básicamente, usted debe hacer lo siguiente:

FILE(GLOB subproject1_sources 
    <sub_project1_lib_sources_dir>/file1.c 
    <sub_project1_lib_sources_dir>/file2.c //... etc 
) 

FILE(GLOB subproject2_sources 
    <sub_project2_lib_sources_dir>/file1.c 
    <sub_project2_lib_sources_dir>/file2.c //... etc 
) 

FILE(GLOB topProject_sources 
    <top_project_lib_sources_dir>/file1.c 
    <top_project_lib_sources_dir>/file2.c //... etc 
) 

include_directories("<sub_project1_lib_sources_dir>") 
include_directories("<sub_project2_lib_sources_dir>") 
include_directories("<top_project_lib_sources_dir>") //should be "." if you're building from here 

add_library(topProject SHARED ${topProject_sources} ${subproject1_sources} ${subproject2_sources}) 
+0

Esto es muy poco probable que sea útil. El problema es que a menudo tienes una mezcla de sistemas de compilación más antiguos que producen archivos .o por alguna magia interna que NO quieres cambiar. Raramente podrá agregar una lista de fuentes en cualquier sistema de producción. –

4

Mi solución es simplemente añadir /WHOLEARCHIVE, -all_load o --whole-archive a las banderas de engarce, de modo que cuando su biblioteca principal está vinculado, todas las bibliotecas sub son . incluido, incluyendo todos sus símbolos (el comportamiento por defecto es sólo para incluir símbolos de las bibliotecas secundarias que son utilizados por la biblioteca principal, por ejemplo:

archivos de origen

$ echo "void Func1() { }" > source1.cpp 
$ echo "void Func2() { }" > source2.cpp 
$ echo "void Func3() { }" > source3.cpp 
$ echo "void Func4() { }" > source4.cpp 

Naive CMakeLists.txt

cmake_minimum_required(VERSION 3.7) 

# The 'sub' libraries, e.g. from an `add_subdirectory()` call. 
add_library(sublib_a STATIC source1.cpp source2.cpp) 
add_library(sublib_b STATIC source3.cpp source4.cpp) 

# The main library that contains all of the sub libraries. 
add_library(mainlib SHARED) 

target_link_libraries(mainlib sublib_a sublib_b) 

Ejecutarlo (en OSX):

$ make VERBOSE=1 
... 
[100%] Linking CXX shared library libmainlib.dylib 
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1 
/Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a 
[100%] Built target mainlib 

$ nm libmainlib.dylib | grep Func 
$ 

CMakeLists.txt correcta

Anexar esto:

# By default, symbols provided by the sublibs that are not used by mainlib (which is all of them in this case) 
# are not used. This changes that. 
if (WIN32) 
    set_target_properties(mainlib PROPERTIES 
     LINK_FLAGS "/WHOLEARCHIVE" 
    ) 
elseif (APPLE) 
    set_target_properties(mainlib PROPERTIES 
     LINK_FLAGS "-Wl,-all_load" 
    ) 
else() 
    set_target_properties(mainlib PROPERTIES 
     LINK_FLAGS "-Wl,--whole-archive" 
    ) 
endif() 

Ejecutarlo (tenga en cuenta el extra -all_load):

$ make VERBOSE=1 
[100%] Linking CXX shared library libmainlib.dylib 
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1 
/Library/Developer/CommandLineTools/usr/bin/c++ -dynamiclib -Wl,-headerpad_max_install_names -Wl,-all_load -o libmainlib.dylib -install_name @rpath/libmainlib.dylib libsublib_a.a libsublib_b.a 
[100%] Built target mainlib 

$ nm libmainlib.dylib | grep Func 
0000000000001da0 T __Z5Func1v 
0000000000001db0 T __Z5Func2v 
0000000000001dc0 T __Z5Func3v 
0000000000001dd0 T __Z5Func4v 

Tenga en cuenta que solo he probado -all_load hasta ahora, y /WHOLEARCHIVE es una opción MSVC 2015.

+0

Estoy en Linux, simplemente agregando '-Wl, - whole-archive' resultó en toneladas de errores de" definiciones múltiples "relacionados con' libgcc.a' – nodakai

+0

Sí, la opción '/ WHOLEARCHIVE' no parece funcionar bien bien, así que simplemente fui con el enfoque de biblioteca de objetos. – Timmmm

Cuestiones relacionadas