Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Systèmes de compilation Discussion :

[cmake] installation d'une bibliothèque précompilée.


Sujet :

Systèmes de compilation

  1. #1
    Expert éminent sénior
    [cmake] installation d'une bibliothèque précompilée.
    Bonjour à tous.
    Je travaille sur deux projets:
    1. Un SDK matérialisé par une bibliothèque partagée ou statique.
    2. Des applications clientes tournant sur plusieurs plateforme, dont Linux et Windows.


    La bibliothèque est gérée avec CMake et doit être compilable avec GCC sous Linux et Visual Studio sous Windows.
    L'application cliente où je rencontre mon problème est compilée sous Windows

    Le SDK installe ses cibles de compilations, et exporte sa configuration d'installation (install(TARGETS SDK EXPORT SDKConfig...) et install(EXPORT SDKConfig ...)).
    Le client utilise find_package(SDK) puis target_link_library(SDK).

    Jusque là, tout va bien, c'est la procédure standard.

    Mon problème se pose avec un détail du SDK.
    La bibliotèque principale du SDK dépend d'une autre, que j'appelerai ici libinterne, compilée en "static mais fpic".
    CMake exige que j'installe aussi cette target libinterne, ce que j'ai fait (install(TARGETS libinterne EXPORT SDKConfig...)).

    Pour la version Linux, la discussion s'arrête là et tout va bien.
    Pour la version windows, il s'avère que libinterne utilise une seconde sous-bibliothèque, détail.lib, dont nous n'avons pas les sources.

    Voici le CMakeLists.txt de libinterne, qui est "add_subdirectory" par celui du SDK. (j'ai seulement retiré les elseif pour les autres plateformes)
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    add_library(libinterne STATIC ${SOURCES})
    target_include_directories(libinterne
    PUBLIC
      ${libinterne_INCLUDES}
    )
     
    if (WIN32)
      set(DETAIL_DIR "un chemin dans les sources")
      target_link_libraries(libinterne
      PRIVATE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${DETAIL_DIR}/détail.lib>
        # $<INSTALL_INTERFACE:détail.lib>
      )
     
      # install(FILES ${DETAIL_DIR}/détail.lib DESTINATION lib)
    endif()
     
    install(TARGETS libinterne
      EXPORT SDKConfig
      RUNTIME DESTINATION bin
      LIBRARY DESTINATION lib
      ARCHIVE DESTINATION lib
    )


    En l'état, le SDK est compilé et installé avec libinterne, mais sans détail.lib.
    L'application cliente se plaint que certains symboles externes ne sont pas définis:
    libinterne.lib(libinterne-win32.obj) : error LNK2019: unresolved external symbol "unsigned char __cdecl DETAIL_fonction1(char *)" (?DETAIL_fonction1@@YAEPAD@Z) referenced in function "long __cdecl Interne::f1(unsigned long,unsigned long)" (?f1@Interne@@YAJKK@Z) []
    Si je décommente les deux lignes commentées, l'édition de liens de l'application cliente échoue car
    fatal error LNK1104: cannot open file 'détail.lib []'
    À noter, j'ai une autre paire de bibliothèque A et B, qui fonctionnent de la même manière, sans poser problème (SDK dépend de A qui dépend de B, mais B est compilée par le projet).

    Je ne comprends pas plusieurs points, à savoir:
    • Pourquoi le PRIVATE n'est pas honoré: pourquoi l'application cliente doit linker sur détail.lib.
    • Pourquoi les symboles de détail.lib ne sont pas correctement inclus dans libinterne, alors qu'il s'agit de deux bibliothèque statiques.
    • Comment installer détail.lib pour qu'elle soit liée automatiquement, de la même manière que les autres bibliothèques (comme B).


    Du coup, je ne vois pas comment m'en sortir.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  2. #2
    Expert éminent sénior
    Après longue discussion, on est arrivé à:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
      set(DETAIL_DIR "un chemin dans les sources")
      target_link_libraries(libinterne
      PRIVATE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${DETAIL_DIR}/détail.lib>
        $<INSTALL_INTERFACE:\${_IMPORT_PREFIX}/lib/détail.lib>
      )
     
      install(FILES ${DETAIL_DIR}/détail.lib DESTINATION lib)

    Le fichier de configuration contient bien target_link_library(${_IMPORT_PREFIX}/lib/détail.lib), exactement comme si la bibliothèque avait été compilée et installée par CMake

    Je ne suis pas sûre que c'est la bonne solution, mais s'en est une qui répond au besoin, et pas trop moche
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle