2013-05-06 10 views
53

Immaginate il seguente scenario: il Progetto A è una libreria condivisa che ha diverse dipendenze (LibA, LibB, LibC). Il Progetto B è un eseguibile che ha una dipendenza dal progetto A e quindi richiede anche tutte le dipendenze del Progetto A per poterlo costruire.CMake e ricerca di altri progetti e delle loro dipendenze

Inoltre, entrambi i progetti vengono creati utilizzando CMake e il progetto A non deve essere installato (tramite la destinazione "Installa") affinché il progetto B possa utilizzarlo, in quanto ciò può diventare una seccatura per gli sviluppatori.

Quindi la domanda è: qual è il modo migliore per risolvere queste dipendenze utilizzando CMake? La soluzione ideale sarebbe la più semplice possibile (anche se non più semplice) e richiederebbe una manutenzione minima.

+2

Per prosperità futura: http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file – blockchaindev

+0

Questo tutorial non sembra spiegare come gestire le dipendenze delle librerie esterne di esportazione. L'unica libreria collegata è una costruita dal progetto. Ho bisogno di sapere come dire al Progetto B che il Progetto A richiede varie librerie esterne e quindi questi devono essere aggiunti alla fase di collegamento del Progetto B. –

risposta

103

Facile. Ecco l'esempio dalla parte superiore della mia testa:

Il livello superiore CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.10) 

# You can tweak some common (for all subprojects) stuff here. For example: 

set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) 
set(CMAKE_DISABLE_SOURCE_CHANGES ON) 

if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 
    message(SEND_ERROR "In-source builds are not allowed.") 
endif() 

set(CMAKE_VERBOSE_MAKEFILE ON) 
set(CMAKE_COLOR_MAKEFILE ON) 

# Remove 'lib' prefix for shared libraries on Windows 
if (WIN32) 
    set(CMAKE_SHARED_LIBRARY_PREFIX "") 
endif() 

# When done tweaking common stuff, configure the components (subprojects). 
# NOTE: The order matters! The most independent ones should go first. 
add_subdirectory(components/B) # B is a static library (depends on Boost) 
add_subdirectory(components/C) # C is a shared library (depends on B and external XXX) 
add_subdirectory(components/A) # A is a shared library (depends on C and B) 

add_subdirectory(components/Executable) # Executable (depends on A and C) 

CMakeLists.txt in components/B:

cmake_minimum_required(VERSION 2.8.10) 

project(B C CXX) 

find_package(Boost 
      1.50.0 
      REQUIRED) 

file(GLOB CPP_FILES source/*.cpp) 

include_directories(${Boost_INCLUDE_DIRS}) 

add_library(${PROJECT_NAME} STATIC ${CPP_FILES}) 

# Required on Unix OS family to be able to be linked into shared libraries. 
set_target_properties(${PROJECT_NAME} 
         PROPERTIES POSITION_INDEPENDENT_CODE ON) 

target_link_libraries(${PROJECT_NAME}) 

# Expose B's public includes (including Boost transitively) to other 
# subprojects through cache variable. 
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include 
           ${Boost_INCLUDE_DIRS} 
    CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/C:

cmake_minimum_required(VERSION 2.8.10) 

project(C C CXX) 

find_package(XXX REQUIRED) 

file(GLOB CPP_FILES source/*.cpp) 

add_definitions(${XXX_DEFINITIONS}) 

# NOTE: Boost's includes are transitively added through B_INCLUDE_DIRS. 
include_directories(${B_INCLUDE_DIRS} 
        ${XXX_INCLUDE_DIRS}) 

add_library(${PROJECT_NAME} SHARED ${CPP_FILES}) 

target_link_libraries(${PROJECT_NAME} B 
             ${XXX_LIBRARIES}) 

# Expose C's definitions (in this case only the ones of XXX transitively) 
# to other subprojects through cache variable. 
set(${PROJECT_NAME}_DEFINITIONS ${XXX_DEFINITIONS} 
    CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) 

# Expose C's public includes (including the ones of C's dependencies transitively) 
# to other subprojects through cache variable. 
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include 
           ${B_INCLUDE_DIRS} 
           ${XXX_INCLUDE_DIRS} 
    CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/A:

cmake_minimum_required(VERSION 2.8.10) 

project(A C CXX) 

file(GLOB CPP_FILES source/*.cpp) 

# XXX's definitions are transitively added through C_DEFINITIONS. 
add_definitions(${C_DEFINITIONS}) 

# NOTE: B's and Boost's includes are transitively added through C_INCLUDE_DIRS. 
include_directories(${C_INCLUDE_DIRS}) 

add_library(${PROJECT_NAME} SHARED ${CPP_FILES}) 

# You could need `${XXX_LIBRARIES}` here too, in case if the dependency 
# of A on C is not purely transitive in terms of XXX, but A explicitly requires 
# some additional symbols from XXX. However, in this example, I assumed that 
# this is not the case, therefore A is only linked against B and C. 
target_link_libraries(${PROJECT_NAME} B 
             C) 

# Expose A's definitions (in this case only the ones of C transitively) 
# to other subprojects through cache variable. 
set(${PROJECT_NAME}_DEFINITIONS ${C_DEFINITIONS} 
    CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) 

# Expose A's public includes (including the ones of A's dependencies 
# transitively) to other subprojects through cache variable. 
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include 
           ${C_INCLUDE_DIRS} 
    CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 

CMakeLists.txt in components/Executable:

cmake_minimum_required(VERSION 2.8.10) 

project(Executable C CXX) 

file(GLOB CPP_FILES source/*.cpp) 

add_definitions(${A_DEFINITIONS}) 

include_directories(${A_INCLUDE_DIRS}) 

add_executable(${PROJECT_NAME} ${CPP_FILES}) 

target_link_libraries(${PROJECT_NAME} A C) 

Per rendere più chiaro, qui è la struttura corrispondente sorgenti:

Root of the project 
├───components 
│ ├───Executable 
│ │ ├───resource 
│ │ │ └───icons 
│ │ ├───source 
| | └───CMakeLists.txt 
│ ├───A 
│ │ ├───include 
│ │ │ └───A 
│ │ ├───source 
| | └───CMakeLists.txt 
│ ├───B 
│ │ ├───include 
│ │ │ └───B 
│ │ ├───source 
| | └───CMakeLists.txt 
│ └───C 
│  ├───include 
│  │ └───C 
│  ├───source 
|  └───CMakeLists.txt 
└───CMakeLists.txt 

Ci sono molti punti in cui questo potrebbe essere modificato/personalizzato o modificato per soddisfare alcune ne eds, ma questo dovrebbe almeno iniziare.

NOTA: Ho impiegato con successo questa struttura in diversi progetti di medie e grandi dimensioni.

+11

Sei una F **** STAR! mi hai davvero tirato fuori da un forte mal di testa che è durato quasi un giorno. Grazie mille 1 – rsacchettini

+1

Sono curioso, sarebbe compilato se dovessi invocare il Cmake dalla directory "Executable"; o dovrei compilare sempre dalla radice del progetto? –

+1

Penso che abbia il piccolo inconveniente che si definisce in ciascun progetto le directory di inclusione due volte (una volta per 'set (... INCLUDE_DIRS' e una volta per' include_directories() '), che trovo difficile da mantenere (ricordando sempre di aggiungi una nuova dipendenza di inclusione in * due * posti). Puoi metterli in pratica con 'get_property (... PROPRIETÀ INCLUDE_DIRECTORIES)'. – pseyfert

Problemi correlati