web-dev-qa-db-fra.com

CMake: Comment construire des projets externes et inclure leurs objectifs

J'ai un projet A qui exporte une bibliothèque statique en tant que cible:

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)

Maintenant, je veux utiliser le projet A en tant que projet externe du projet B et inclure ses cibles construites:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

Le problème est que le fichier d'inclusion n'existe pas encore lorsque CMakeLists du projet B est exécuté.

Existe-t-il un moyen de rendre l’inclusion dépendante du projet externe en cours de construction?

74
mirkokiefer

Je pense que vous mélangez deux paradigmes différents ici.

Comme vous l'avez indiqué, le très flexible ExternalProject module exécute ses commandes au moment de la construction. Vous ne pouvez donc pas utiliser directement le fichier d'importation du projet A, car il n'a été créé qu'une fois ce dernier installé.

Si vous voulez include le fichier d'importation du projet A, vous devez avoir installer le projet A manuellement avant d'appeler le fichier CMakeLists.txt du projet B - comme toute autre dépendance tierce ajoutée de cette manière ou via find_file/find_library/find_package.

Si vous souhaitez utiliser ExternalProject_Add, vous devez ajouter à votre fichier CMakeLists.txt quelque chose de similaire:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)
53
Fraser

Cet article a une réponse raisonnable:

CMakeLists.txt.in:

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

Cependant, cela semble assez hacky. J'aimerais proposer une solution alternative - utiliser des sous-modules Git.

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

Ensuite, dans MyProject/dependencies/gtest/CMakeList.txt, vous pouvez faire quelque chose comme:

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

Je n'ai pas encore beaucoup essayé, mais cela semble plus propre.

Edit: Cette approche présente les inconvénients suivants: le sous-répertoire peut exécuter des commandes install() que vous ne souhaitez pas. Ce message a une approche pour les désactiver mais c'était bogué et n'a pas fonctionné pour moi.

Edit 2: Si vous utilisez add_subdirectory("googletest" EXCLUDE_FROM_ALL), il semble que les commandes install() du sous-répertoire ne sont pas utilisées par défaut.

13
Timmmm

Vous pouvez également forcer la construction de la cible dépendante dans un processus de création secondaire.

Voir ma réponse sur un sujet connexe.

4
David

Ce que vous pouvez essayer de faire est d’utiliser la commande export de cmake dans votre project_a. Cela fonctionne un peu différemment que d'utiliser la commande install avec le EXPORT option en ce sens qu'il génère votre fichier project_a-targets.cmake lors de l'exécution de cmake. Les cibles d'importation générées dans le fichier project_a-targets.cmake désignent initialement les fichiers de bibliothèque non existants dans le répertoire binaire de votre projet qui ne seront générés qu'après l'exécution de la commande de construction.

Pour mieux comprendre ce dont je parle, créez simplement un petit projet cmake qui crée une bibliothèque simple suivie de la commande export (le code ci-dessous n'a pas été testé):

add_library (project_a lib.cpp)
export (
  TARGETS 
    project_a
  FILE
    project_a-targets.cmake
)

Après avoir exécuté la commande cmake sur votre exemple simple, vous devriez pouvoir trouver le project_a-targets.cmake dans votre répertoire binaire (ou sous l'un de ses dossiers enfants). En inspectant le fichier, vous remarquerez peut-être qu'il pointe actuellement vers un fichier de bibliothèque inexistant. La bibliothèque ne sera là qu'après l'exécution de la commande de construction.

Pour revenir à votre problème, vous devrez mettre à jour le project-a du CMakeLists.txt afin d'inclure la commande export. Ensuite, vous devez vous assurer qu’après le traitement du ExternalProject_Add, il appelle l’étape de configuration qui produira le project_a-targets.cmake. Vous pourrez ensuite appeler le include(.../project_a-targets.cmake) qui devrait fonctionner. Enfin, vous devrez ajouter une dépendance entre project_b et project_a afin de forcer la construction de project_a avant de tenter de construire project_b

0
Rodrigo Reichert