it-swarm.com.de

CMake: Wie man externe Projekte aufbaut und ihre Ziele einbezieht

Ich habe ein Projekt A, das eine statische Bibliothek als Ziel exportiert:

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

Jetzt möchte ich Projekt A als externes Projekt aus Projekt B verwenden und seine erstellten Ziele einschließen:

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)

Das Problem ist, dass die Include-Datei noch nicht vorhanden ist, wenn CMakeLists von Project B ausgeführt wird.

Gibt es eine Möglichkeit, das Include von dem externen Projekt abhängig zu machen, das gerade erstellt wird?

74
mirkokiefer

Ich denke, Sie mischen hier zwei verschiedene Paradigmen.

Wie Sie bereits festgestellt haben, führt das äußerst flexible ExternalProject -Modul seine Befehle zur Erstellungszeit aus, sodass Sie die Importdatei von Project A nicht direkt verwenden können, da sie erst erstellt wird, nachdem Project A installiert wurde.

Wenn Sie include die Importdatei von Project A erstellen möchten, müssen Sie haben, um Project A manuell zu installieren, bevor Sie CMakeLists.txt von Project B aufrufen - genau wie jede auf diese Weise hinzugefügte Abhängigkeit von Drittanbietern oder über find_file/find_library/find_package.

Wenn Sie ExternalProject_Add verwenden möchten, müssen Sie Ihrer CMakeLists.txt Folgendes hinzufügen:

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

Dieser Beitrag hat eine vernünftige Antwort:

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

Es scheint jedoch ziemlich hackig zu sein. Ich möchte eine alternative Lösung vorschlagen - verwenden Sie Git-Submodule.

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"

Dann können Sie in MyProject/dependencies/gtest/CMakeList.txt so etwas tun:

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")

Ich habe das noch nicht ausgiebig ausprobiert, aber es scheint sauberer zu sein.

Bearbeiten: Dieser Ansatz hat einen Nachteil: Das Unterverzeichnis kann install()-Befehle ausführen, die Sie nicht möchten. Dieser Beitrag hat einen Ansatz, um sie zu deaktivieren aber es war fehlerhaft und funktionierte nicht für mich.

Bearbeiten 2: Wenn Sie add_subdirectory("googletest" EXCLUDE_FROM_ALL) verwenden, werden die install()-Befehle im Unterverzeichnis standardmäßig nicht verwendet.

13
Timmmm

Sie können das Erstellen des abhängigen Ziels auch in einem sekundären Herstellungsprozess erzwingen

Siehe meine Antwort zu einem verwandten Thema.

4
David

Sie könnten versuchen, dies zu tun, indem Sie den Befehl cmake export in Ihrem project_a verwenden. Es funktioniert ein wenig anders als wenn Sie den Befehl install mit dem Code EXPORT option verwenden, indem Sie beim Ausführen von cmake eine project_a-targets.cmake-Datei erstellen. Die generierten Importziele in der project_a-targets.cmake-Datei verweisen auf die nicht vorhandenen Bibliotheksdateien im Binärverzeichnis Ihres Projekts, die erst generiert werden, nachdem Sie den Befehl build ausgeführt haben.

Um besser zu verstehen, worüber ich spreche, erstellen Sie einfach ein kleines cmake-Projekt, das eine einfache Bibliothek erstellt, gefolgt vom Exportbefehl (der Code wurde nicht getestet):

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

Nachdem Sie den Befehl cmake in Ihrem einfachen Beispiel ausgeführt haben, sollten Sie project_a-targets.cmake in Ihrem Binärverzeichnis (oder unter einem seiner untergeordneten Ordner) finden können. Beim Überprüfen der Datei stellen Sie möglicherweise fest, dass sie derzeit auf eine nicht vorhandene Bibliotheksdatei verweist. Erst nachdem der Build-Befehl ausgeführt wurde, ist die Bibliothek vorhanden.

Um zu Ihrem Problem zurückzukehren, müssen Sie den project-a von CMakeLists.txt aktualisieren, um den Befehl export einzuschließen. Dann müssen Sie sicherstellen, dass nach der Verarbeitung von ExternalProject_Add der Konfigurationsschritt aufgerufen wird, der den project_a-targets.cmake erzeugt. Dann können Sie include(.../project_a-targets.cmake) aufrufen, das funktionieren sollte. Schließlich müssen Sie eine Abhängigkeit zwischen project_b und project_a hinzufügen, damit project_a erstellt werden muss, bevor Sie project_b erstellen. 

0