Skip to main content

Introduzione a CMake Parte 2

Posted in

Nella prima parte di questa guida abbiamo spiegato come gestire un semplice progetto, ora affrontiamo la stesura di un progetto più complesso che necessiti di una maggiore granularità e la generazione di librerie statiche.

Il caso di studio più semplice è costituito da una directory principale con alcune sottodirectory che si occupino dei diversi aspetti del progetto, per esempio:

MyProject/
-core/
--CMakeLists.txt
--core.cpp

--core.h
-gui/
--CMakeLists.txt
--gui.cpp
--gui.h
--gui.ui
-main.cpp
-CMakeLists.txt

Abbiamo un progetto diviso in due sottodirectory: core e gui, ogni sottodirectory deve contenere un suo CMakeLists.txt.

Prima di illustrare come aggiungere le sottodirectory conviene analizzare i CMakeLists.txt presenti al loro interno. Nel caso più semplice, cioè quando dobbiamo solo creare un codice oggetto da linkare staticamente 
all'eseguibile finale, il CMkaeLists.txt apparirà come una versione ridotta di quello principale, infatti le subdirectory ereditano variabili e impostazioni del CMakeLists.txt che le ha aggiunte.

Il CMakeLists.txt all'interno di core avrà le seguenti righe:

set(core_includes
      ${CMAKE_SOURCE_DIR}
      ${CMAKE_BINARY_DIR}
       ${CMAKE_CURRENT_SOURCE_DIR}
       ${CMAKE_CURRENT_BINARY_DIR}

)
Queste ultime due variabili sono impostate da CMake e puntano alle directory correnti, MyProject/core per il sorgente, e <directory di build>/core per quella dove compiliamo.

include_directories(core_includes) 

include(${QT_USE_FILE})

set(core_srcs core.cpp)

set( core_hdrs
     core.h
)

qt4_wrap_cpp( core_hdrs ${core_mocs} )

add_library
(core STATIC ${core_mocs} ${core_srcs})

Quello in gui sarà analogo con l'aggiunta dell'elaborazione del file del Designer:

include_directories(gui_includes) 

include(${QT_USE_FILE})

set(gui_srcs core.cpp)

set( gui_hdrs
     gui.h
)

qt4_wrap_cpp( gui_hdrs ${core_mocs} )

set( gui_uis
     gui.ui
)

qt4_wrap_ui( gui_uis ${gui_ui_hdrs} )
add_library(gui STATIC ${gui_srcs} ${gui_mocs} ${gui_ui_hrds})

In pratica si tratta di cose già affrontate in precedenza, solo che ora le incapsuliamo in una directory e creiamo una libreria statica.

Torniamo al CMakeLists.txt e aggiungiamo le due directory, per farlo si utilizza il comando
add_subdirectory(NOME_DIRECTORY)

nel nostro caso:
add_subdirectory(core)
add_subdirectory(gui)

aggiunte le directory non ci rimane che linkare le librerie all'eseguibile:

target_link_libraries(myproject core gui ${QT_LIBRARIES})   

ATTENZIONE: core e gui non si riferiscono al nome delle directory ma alla libreria costruita con il comando add_library(), in questo esempio, per semplicità, condividono lo stesso nome.

Per completezza sarebbe opportuno includere il path alle sottodirectory nella lista degli includes, specialmente quelle che puntano alle directory di compilazione:
set(myproject_includes
 ${CMAKE_SOURCE_DIR}
 ${CMAKE_BINARY_DIR}
 ${CMAKE_SOURCE_DIR}/core
 ${CMAKE_BINARY_DIR}/core
 ${CMAKE_SOURCE_DIR}/gui
 ${CMAKE_BINARY_DIR}/gui
 )
ricordiamo che uic e moc creano dei file a compile-time, cmake compila out-of-source e quindi questi file vanno resi raggiungibili puntando alla directory di compilazione.

Il file CMakeLists.txt principale assumerà la seguente forma:
project(myproject)

find_package(Qt4 REQUIRED)

set(myproject_includes
 ${CMAKE_SOURCE_DIR}
 ${CMAKE_BINARY_DIR}
 ${CMAKE_SOURCE_DIR}/core
 ${CMAKE_BINARY_DIR}/core
 ${CMAKE_SOURCE_DIR}/gui
 ${CMAKE_BINARY_DIR}/gui
 )

include_directories(myproject_includes) 

include(${QT_USE_FILE})

add_subdirectory(core)
add_subdirectory(gui)

set
(myproject_srcs main.cpp)

add_executable
(myproject ${myproject_srcs})

target_link_libraries(myproject core gui ${QT_LIBRARIES})   

CMake processa le directory non appena le incontra, quindi processerà prima core e poi gui, se core avesse al suo interno altre directory processerebbe prima queste e poi continuerebbe con gui.

Avrete notato che nei CMakeLists.txt delle sottodirectory non è presente il linking alle librerie Qt, essendo statiche basta linkarle una volta per tutte durante la costruzione dell'eseguibile. Attenzione, però, quando si utilizzano altre librerie solo in quelle directory, dovranno essere cercate con find_package() e linkate appositamente con il consueto comando "target_link_libraries()".
In allegato un file zippato con un progetto di esempio:
myproject.zip

La seconda parte finisce qui, con questa infarinatura sarete capaci di gestire un progetto più complesso e meglio organizzato.
Nel prossimo capitolo affronteremo le librerie dinamiche, come realizzarle, installarle e renderle accessibili ad altri progetti basati su CMake.
 

Daniele Granata
Creative Commons License