web-dev-qa-db-fra.com

Combinaison de plusieurs bibliothèques statiques en une seule à l'aide de CMake

J'ai un problème très similaire à un décrit de la liste de diffusion cmake où nous avons un projet dépendant de nombreuses bibliothèques statiques (toutes construites à partir de sources dans des sous-modules individuels, chacune avec son propre fichier CMakeLists.txt décrivant le processus de construction. pour chaque bibliothèque) que je voudrais combiner en une seule bibliothèque statique pour la publication aux consommateurs. Les dépendances de ma bibliothèque sont sujettes à modification et je ne souhaite pas imposer de modifications à ces développeurs. La solution intéressante consisterait à regrouper toutes les bibliothèques dans une seule bibliothèque.

Il est intéressant de noter que la commande target_link_libraries ne combine pas toutes les statistiques lors du paramétrage de la cible sur mylib et de son utilisation. . 

target_link_libraries(mylib a b c d)

Cependant, bizarrement, si je fais du projet mylib un sous-module d'un projet exécutable et que je ne crée un lien qu'avec mylib dans l'exécutable de niveau supérieur CMAkeLists.txt, la bibliothèque semble être combinée. C'est à dire. mylib est de 27 Mo, au lieu de 3 Mo lorsque je configure la cible pour ne construire que mylib.

Il existe des solutions décrivant la décompression des bibliothèques dans des fichiers objets et la recombinaison ( ici , et ici ), mais cela semble remarquablement maladroit quand CMake semble parfaitement capable de fusionner automatiquement les bibliothèques comme décrit dans l'exemple ci-dessus. Y-a-t-il une commande magique qui me manque ou un moyen élégant recommandé de créer une bibliothèque de publication?

15
learnvst

À partir de l'exemple de travail le plus simple auquel je puisse penser: 2 classes, a et b, où a dépend de b. .

a.h

#ifndef A_H
#define A_H

class aclass
{
public:
    int method(int x, int y);
};

#endif

a.cpp

#include "a.h"
#include "b.h"

int aclass::method(int x, int y) {
    bclass b;
    return x * b.method(x,y);
}

b.h

#ifndef B_H
#define B_H

class bclass
{
public:
    int method(int x, int y);
};

#endif

b.cpp

#include "b.h"

int bclass::method(int x, int y) {
    return x+y;
}

main.cpp

#include "a.h"
#include <iostream>

int main()
{
    aclass a;
    std::cout << a.method(3,4) << std::endl;

    return 0;
}

Il est possible de les compiler dans des bibliothèques statiques distinctes, puis de les combiner à l'aide d'une cible personnalisée.

cmake_minimum_required(VERSION 2.8.7)

add_library(b b.cpp b.h)
add_library(a a.cpp a.h)
add_executable(main main.cpp)

set(C_LIB ${CMAKE_BINARY_DIR}/libcombi.a)

add_custom_target(combined
        COMMAND ar -x $<TARGET_FILE:a>
        COMMAND ar -x $<TARGET_FILE:b>
        COMMAND ar -qcs ${C_LIB} *.o
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        DEPENDS a b
        )

add_library(c STATIC IMPORTED GLOBAL)
add_dependencies(c combined)

set_target_properties(c
        PROPERTIES
        IMPORTED_LOCATION ${C_LIB}
        )

target_link_libraries(main c)

Cela fonctionne aussi très bien avec la version libtool de la cible personnalisée d’Apple. . .

add_custom_target(combined
        COMMAND libtool -static -o ${C_LIB} $<TARGET_FILE:a> $<TARGET_FILE:b>
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        DEPENDS a b
        )

Toujours comme s'il devait y avoir un chemin plus propre. .

9
learnvst

Si les bibliothèques que vous essayez de fusionner proviennent de tiers, alors (suivant l'exemple learnvst), ce code prend en charge d'éventuels remplacements de fichiers .o (si par exemple, liba et libb ont un nom de fichier zzz.o)

## Create static library (by joining the new objects and the dependencies)
ADD_LIBRARY("${PROJECT_NAME}-static" STATIC ${SOURCES})
add_custom_command(OUTPUT lib${PROJECT_NAME}.a
                   COMMAND rm ARGS -f *.o
                   COMMAND ar ARGS -x ${CMAKE_BINARY_DIR}/lib${PROJECT_NAME}-static.a
                   COMMAND rename ARGS 's/^/lib${PROJECT_NAME}-static./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND ar ARGS -x ${CMAKE_SOURCE_DIR}/lib/a/liba.a
                   COMMAND rename ARGS 's/^/liba./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND ar ARGS -x ${CMAKE_SOURCE_DIR}/lib/b/libb.a
                   COMMAND rename ARGS 's/^/libb./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND rename ARGS 's/\\.otmp/.o/g' *.otmp
                   COMMAND ar ARGS -r lib${PROJECT_NAME}.a *.o
                   COMMAND rm ARGS -f *.o
                   DEPENDS "${PROJECT_NAME}-static")

add_custom_target(${PROJECT_NAME} ALL DEPENDS lib${PROJECT_NAME}.a)

Sinon, si les bibliothèques vous appartiennent, vous devriez utiliser les bibliothèques CMake OBJECT, ce qui est un très bon mécanisme pour les fusionner.

3
debuti

Vous pouvez utiliser cette fonction pour rejoindre un nombre illimité de bibliothèques.

function(combine_archives output_archive list_of_input_archives)
    set(mri_file ${TEMP_DIR}/${output_archive}.mri)
    set(FULL_OUTPUT_PATH ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${output_archive}.a)
    file(WRITE ${mri_file} "create ${FULL_OUTPUT_PATH}\n")
    FOREACH(in_archive ${list_of_input_archives})
        file(APPEND ${mri_file} "addlib ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${in_archive}.a\n")
    ENDFOREACH()
    file(APPEND ${mri_file} "save\n")
    file(APPEND ${mri_file} "end\n")

    set(output_archive_dummy_file ${TEMP_DIR}/${output_archive}.dummy.cpp)
    add_custom_command(OUTPUT ${output_archive_dummy_file}
                       COMMAND touch ${output_archive_dummy_file}
                       DEPENDS ${list_of_input_archives})

    add_library(${output_archive} STATIC ${output_archive_dummy_file})
    add_custom_command(TARGET ${output_archive}
                       POST_BUILD
                       COMMAND ar -M < ${mri_file})
endfunction(combine_archives)

Il présente les avantages d'utiliser add_custom_command et non pas add_custom_target. De cette façon, la bibliothèque (et ses dépendances) ne sont construites que lorsque cela est nécessaire et pas à chaque fois . L'inconvénient est l'impression de la génération du fichier factice.

0
zbut