Skip to main content

Introduzione a CMake Parte 1

Posted in

Qt ha un suo gestore di progetto chiamato qmake, è multipiattaforma, molto più semplice di automake autotools e funziona in maniera egregia.
Da qualche anno Kitware inc ha creato un nuovo tool chiamato CMake che ha le stesse caratteristiche di qmake, multipiattaforma e semplicità, ma aiuta a lavorare meglio con librerie esterne a Qt.
La prima novità che introduce CMake è quella della compilazione esterna all'albero del sorgente (out-of-source build), questo fa si che tutti i file temporanei e le librerie generate siano stipate in una directory esterna lasciando l'albero del sorgente intatto e quindi inutile l'invocazione di "make clean" per ripulirlo. 
Ultimamente CMake si è diffuso a macchia d'olio divenendo il tool di riferimento per molti progetti tra cui SecondLife, MySql, KDE4.

Questo tutorial si occuperà di una breve introduzione a CMake, il minimo indispensabile per costruire un progetto Qt.

Il file di configurazione del progetto si chiama CMakeLists.txt e deve essercene uno in ogni directory che compone il progetto.

Di seguito indichiamo le varie parti che compongono il CMakeLists.txt principale con relativa spiegazione :
Prima di tutto diamo un nome al progetto:

# commenti iniziano con un '#'
project(helloworld)

diciamo a CMake di recuperare i dati utili all'utilizzo delle librerie esterne al progetto, in questo caso le Qt:

find_package(Qt4 REQUIRED)

find_package() è una macro che cerca il file FindQt4.cmake, questi identifica i path per le librerie e gli headers di Qt e imposta due variabili 
QT_INCLUDES per gli header e QT_LIBRARIES per le librerie. L'installazione di default di CMake contiene una moltitudine di file FindXXX.cmake per rintracciare sul sistema la maggior parte delle librerie di uso comune.
L'attributo 'REQUIRED' dopo il nome del package da cercare impone a CMake di bloccarsi se lo stesso non dovesse essere stato trovato.
Generalmente le variabili ritornate da find_package() sono costruite con il nome del package seguito da '_INCLUDES' o '_INCLUDE_DIRS' per gli headers e '_LIBRARIES' o '_LIBRARY' per le libs o dll.  
In CMake si fa riferimento a una variabile includendola tra parentesi graffe '{}' e precedendole da simbolo del dollaro'$'
${QT_INCLUDES}
${QT_LIBRARIES}
In fase di definizione si usa solo il suo nome:

set(mia_variabile 3)
 
mia_variabile contiene 3 e per usarla occorre indicarla come descritto in precedenza ${mia_variabile}.

Le variabili in CMake posso contenere tipi semplici come interi e stringhe, e tipi complessi come le liste:

set(sorgenti main.cpp helloworld.cpp)

abbiamo appena definito una lista di stringhe che identificano i file di un progetto.

Continuando con l'impostazione del file CMakeLists.txt ora dobbiamo definire i file del sorgente:

set(helloworld_srcs main.cpp helloworld.cpp)

Definiamo i vari includes:

set(hellowold_includes ${QT_INCLUDES} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} )
${CMAKE_SOURCE_DIR} è una variabile automatica che indica la directory principale del progetto.
${CMAKE_BINARY_DIR} è una variabile automatica che indica la directory principale dove viene compilato il progetto.
Dato che CMake compila fuori sorgente tutti i file temporanei vengono creati nella dir di compilazione, i file moc per esempio finiscono lì. 

Indichiamo a CMake dove cercare i file di include.

include_directories(${helloworld_includes}) 

Poiché CMake non conosce moc dobbiamo impostare a mano gli headers che contengono un Q_OBJECT e dirgli di preprocessarli con moc
 
set(helloworld_hdrs helloworld.h)

qt4_wrap_cpp(helloworld_hdrs_moc ${helloworld_hdrs}) 

qt4_wrap_cpp() è una macro che richiama moc su tutti i file della lista helloworld_hdrs e crea una variabile helloworld_moc_hdrs che contiene i file restituiti da moc.

Impostiamo alcuni flags di compilazione standard per le Qt:
add_definitions(${QT_DEFINITIONS})

Ora creiamo l'eseguibile utilizzando i sorgenti e i moc:

add_executable(helloworld ${helloworld_srcs} ${helloworld_moc_hdrs})

Linkiamo l'eseguibile alle librerie esterne:

target_link_libraries(helloworld ${QT_LIBRARIES})   

FindQt4.cmake definisce molte altri variabili tra cui QT_USE_FILE, questa variabile contiene un nome file che gestisce alcune cose in automatico come i flags di compilazione e gli includes.
Il file CMakeFiles.txt di prima si può riscrivere così:

project(helloworld)

find_package(Qt4 REQUIRED)

set(hellowold_includes ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} )

include_directories(helloworld_includes) 

set(helloworld_hdrs helloworld.h)

qt4_wrap_cpp(helloworld_mocs ${helloworld_hdrs}) 
 
include(${QT_USE_FILE})

set(helloworld_srcs main.cpp helloworld.cpp)

add_executable(helloworld ${helloworld_srcs} ${helloworld_mocs})

target_link_libraries(helloworld ${QT_LIBRARIES})   

Nel caso ci servissero dei componenti extra come QtSql o QtXml dobbiano indicarli a CMake, per far ciò ci sono 2 modi:

in find_package indichiamo i componenti richiesti:

find_package(Qt4 COMPONENTS QtCore QtSql QtXml REQUIRED)
QT_USE_FILE imposterà tutto il resto.

oppure settiamo su TRUE le variabili corrispondenti:
set(QT_USE_QTSQL TRUE)
set(QT_USE_QTXML TRUE)

Nel caso abbiate dei file generati dal designer occorre indicare a CMake come elaborarli

set(helloworld_uis helloworld.ui)
qt4_wrap_ui(helloworld_ui_hdrs ${helloworld_uis}) 

la variabile risultante andrà aggiunta all'eseguibile
add_executable(helloworld ${helloworld_srcs} ${helloworld_mocs} ${helloworld_ui_hdrs})

Analogamente per i file resource:

set(helloworld_qrcs helloworld.qrc)
qt4_add_resources(helloworld_res_qrcs ${helloworld_qrcs}) 

anche in questo caso la variabile risultante andrà aggiunta all'eseguibile
add_executable(helloworld ${helloworld_srcs} ${helloworld_mocs} ${helloworld_ui_hdrs} ${helloworld_res_qrcs})

Una volta impostato il file CMakeLists.txt, per compilare ci spostiamo nella directory di compilazione e invochiamo cmake puntando alla directory sorgente.
Presumendo di avere il seguente albero:

build
helloworld

-CMakeLists.txt

da shell:
cd build
cmake ../helloworld
 
se tutto è andato bene continuiamo con
make

Per chi volesse provare ho allegato un file zippato che contiene un progettino,  con Qt Creator aprite il file CMakeLists.txt, importerà l'intero progetto perché Qt Creator gestisce anche questo tipo di progetti oltre a quelli basati su qmake. 
helloworld.zip

per questa prima parte è tutto, nella prossima affronteremo i temi di installazione e librerie dinamiche e statiche.

Daniele Granata
Creative Commons License