Dit document geeft een snel overzicht van CMake – een cross-platform alternatief voor linux Makefiles en make. Voor hulp bij het gebruik van make en Makefiles zonder CMake, zie Prof. Newhall’s documentatie. Dit document is geschreven voor cmake versie 2.6, die op de lab machines is geïnstalleerd via stow (vanaf Jan 2009).
Ik hou persoonlijk van CMake omdat het intuïtiever lijkt dan het schrijven van een standaard Makefile. Basis en veel voorkomende dingen zijn vrij eenvoudig en moeilijkere dingen zijn mogelijk, hoewel de meeste dingen die ik bouw vrij basic zijn. Om CMake te demonstreren, heb ik wat voorbeeldcode bijgevoegd in het bestand cmake.tgz (4 KB). Sla het bestand op en pak de inhoud ergens in je directory uit. Bijvoorbeeld:
$ tar xzvf cmake.tgz$ cd cmake$ lsCMakeLists.txt w01-cpp/
CMake wordt aangestuurd door instructies te schrijven in CMakeLists.txt bestanden. Elke directory in je project zou een CMakeLists.txt bestand moeten hebben. Het mooie van CMake is dat CMakeLists.txt bestanden in een sub-directory eigenschappen erven die in de hoofd-directory zijn ingesteld, waardoor er minder code gedupliceerd hoeft te worden. Voor ons voorbeeldproject hebben we maar één subdirectory: w01-cpp. Het CMakeLists.txt bestand voor de cmake hoofddirectory is vrij eenvoudig, maar demonstreert een paar belangrijke functies.
cmake_minimum_required(VERSION 2.6)project(CMAKEDEMO)#There are lots of scripts with cmake#for finding external libraries. #see /usr/local/share/cmake-2.6/Modules/Find*.cmake for more examplesfind_package(GLUT)find_package(OpenGL)set(CMAKE_CXX_FLAGS "-g -Wall")add_subdirectory(w01-cpp)
Deze demo code bevat code die externe OpenGL libraries nodig heeft voor graphics. In een typische Makefile configuratie zouden we waarschijnlijk moeten specificeren waar de OpenGL header files zijn, b.v., -I/usr/local/include, welke libraries te gebruiken, b.v., -lGL -lGLU -lglut, en de lokatie van de libraries. De locatie van de headerbestanden en bibliotheken varieert waarschijnlijk van installatie tot installatie en van platform tot platform. Er zijn echter meestal enkele standaard plaatsen om te zoeken, en CMake automatiseert dit zoeken door gebruik te maken van find_package macro’s. Dus find_package(GLUT) en find_package(OpenGL) vindt de locatie van de header bestanden en bibliotheken en stelt de meest voorkomende bibliotheken op waarnaar gelinkt wordt in een typisch OpenGL project. CMake heeft ondersteuning voor het vinden van veel pakketten. Zie /usr/local/share/cmake-2.6/Modules/Find*.cmake voor meer voorbeelden. Om een van deze scripts te gebruiken, gebruik je find_package(PKGname) als het CMake script FindPKGname.cmake heet.
Voor meer over het vinden van externe bibliotheken of het maken van je eigen find package macro’s, zie de KitWare wiki over het onderwerp.
Je kunt gewone vlaggen instellen die je in een typisch Makefile zou instellen met behulp vaningset(VARNAME VALUE). In dit voorbeeld heb ik debug-symbolen (-g) en alle waarschuwingen (-Wall) aangezet. Ik heb dit CMake project ook een naam gegeven met project(CMAKEDEMO). Je kunt je project elke naam geven die je wilt. We zullen deze info gebruiken in het CMakeLists.txt bestand in de w01-cpp directory. Tenslotte geven we aan dat de meeste code echt in de w01-cpp staat door ons CMakeLists.txt bestand op het hoogste niveau te vertellen om add_subdirectory(w01-cpp)
Laten we eens kijken naar het CMakeLists.txt bestand in de w01-cpp subdirectory:
include_directories(${CMAKEDEMO_SOURCE_DIR}/w01-cpp)link_directories(${CMAKEDEMO_BINARY_DIR}/w01-cpp)#the one C fileadd_executable(cdemo cdemo.c)target_link_libraries(cdemo m) #link the math library#these are all compiled the same wayset(PROGRAMS oglfirst pointers)set(CORELIBS ${GLUT_LIBRARY} ${OPENGL_LIBRARY} m)foreach(program ${PROGRAMS}) add_executable(${program} ${program}.cpp) target_link_libraries(${program} ${CORELIBS})endforeach(program)#building just a library. add_library(geometry geometry.cpp)add_executable(test_geometry test_geometry.cpp)#linking against a custom librarytarget_link_libraries(test_geometry ${CORELIBS} geometry)
De include_directories macro vertelt CMake waar het moet zoeken naar bronbestanden. Door de projectnaam CMAKEDEMO te declareren in het CMakeLists.txt bestand op het hoogste niveau, worden de variabelen CMAKEDEMO_SOURCE_DIR en CMAKEDEMO_BINARY_DIR ingesteld, afhankelijk van de huidige locatie van je code en de huidige locatie van je bouwdirectory (later meer over deze laatste locatie). Een doel toevoegen voor een nieuwe binaire executable is eenvoudig. Voeg gewoon een regel toe add_executable(cdemo cdemo.c). CMake zal automatisch de compiler bepalen op basis van de bestandstype-extensie. Als bijkomende bibliotheken nodig zijn, kan je CMake vertellen om er tegen te linken met target_link_libraries(cdemo m). Dit vertelt CMake dat het cdemo programma moet linken naar de wiskunde bibliotheek. Je kunt tegen meer dan één bibliotheek linken door de bibliotheken in een lijst op te geven. Zie bijvoorbeeld set(CORELIBS ${GLUT_LIBRARY} ${OPENGL_LIBRARY} m). De variabelen GLUT_LIBRARY en OPENGL_LIBRARY zijn door CMake ingesteld toen we de find_package(GLUT) en find_package(OpenGL) gebruikten. Om uit te vinden welke variabelen zijn ingesteld bij het gebruik van find_package, zult u waarschijnlijk het betreffende Find*.cmake bestand moeten openen en de meestal behulpzame maar beknopte documentatie moeten lezen.
Omdat verschillende programma’s in deze w01-cpp map op dezelfde manier zijn gecompileerd en tegen dezelfde bibliotheken zijn gelinkt, kunnen we ze allemaal verwerken met een eenvoudige lus die hierboven is afgebeeld met behulp van de foreach macro in CMake. Tenslotte laten we zien hoe we onze eigen bibliotheek maken door add_library(geometry geometry.cpp) te gebruiken en tegen deze bibliotheek te linken in target_link_libraries(test_geometry ${CORELIBS} geometry).
Het opzetten van een stel CMakeLists.txt bestanden zal je niet direct in staat stellen om je project te bouwen. CMake is slechts een cross platform wrapper rond meer traditionele bouw systemen. In het geval van Linux, betekent dit make. Een snelle preprocessing stap zal je CMakeLists.txt beschrijving automatisch omzetten naar een traditioneel make build systeem. Een leuke en zeer aan te bevelen eigenschap van CMake is de mogelijkheid om “out of source” builds te doen. Op deze manier kun je al je .o bestanden, diverse tijdelijke depend bestanden, en zelfs de binaire executables maken zonder dat je source tree onoverzichtelijk wordt. Om out of source builds te gebruiken, maak je een build directory aan in je top-level map (technisch gezien kan dit overal, maar de top-level project map lijkt een logische keuze). Ga vervolgens naar je bouwdirectory en voer cmake uit, wijzend naar de directory van de CMakeLists.txt van het hoogste niveau. Bijvoorbeeld:
cumin$ cd cmake/cumin$ lsCMakeLists.txt w01-cpp/cumin$ mkdir buildcumin$ lsCMakeLists.txt build/ w01-cpp/cumin$ cd build/cumin$ cmake ..
Denk eraan dat je in je bouwdirectory bent en wijs cmake alleen naar de directory met het CMakeLists.txt bestand op het hoogste niveau, niet naar het bestand zelf. Als alles goed gaat, zal cmake je CMakeLists.txt bestanden verwerken, de locatie van alle libraries en include paden vinden en een hoop configuratie informatie spuwen, inclusief een traditionele Makefile in je bouwdirectory. (Als je bekend bent met autotools/autohell, dan is dit cmake proces vergelijkbaar met ./configure). Je bent nu klaar om te bouwen met het traditionele make systeem. Start make in je build directory om alles te compileren en te linken. CMake voegt zelfs enkele mooie kleuren en voortgangsbalken toe en onderdrukt een hoop gcc uitvoer. Als je verbose output wilt, kun je VERBOSE=1 make typen (handig als er iets fout gaat).
Omwille van de manier waarop we de “out of source” build hebben opgezet en de link_directories(${CMAKEDEMO_BINARY_DIR}/w01-cpp), staan onze vers gecompileerde binaries in de w01-cpp map in de build directory waar we zojuist make hebben uitgevoerd.
cumin$ cd w01-cpp/cumin$ lsCMakeFiles/ cdemo* libgeometry.a pointers*Makefile cmake_install.cmake oglfirst* test_geometry*cumin$ ./cdemo Sqrt(2) = 1.4142 This concludes a short C democumin$ ./oglfirst cumin$ ./test_geometry
Druk op ESC om de OpenGL demo’s te verlaten.
Als u code in uw broncode directory wijzigt, inclusief zelfs een CMakeLists.txt bestand en make opnieuw uitvoert in de build directory, zullen make en cmake de noodzakelijke wijzigingen hercompileren en opnieuw bouwen. Als je alleen wijzigingen in een subdirectory aanbrengt, kun je make gewoon in de corresponderende subdirectory in de build tree draaien om de updates te verwerken.
Een eerste bron van verwarring met “out of source” builds is dat je in feite twee kopieën van je source tree hebt, een met de eigenlijke broncode, en een met Makefiles en binaire uitvoerbare bestanden (in de build tree). Het is waarschijnlijk het beste om twee vensters open te houden, een in de bouwboom voor het maken en draaien van je programma’s, en een venster in de broncode boom voor het wijzigen van broncode bestanden.
Een leuke eigenschap van out of source builds is dat het opruimen van object bestanden, makedepend bestanden, binaries, en andere diverse bouwafval gedaan kan worden door simpelweg de hele bouwmap te verwijderen omdat er geen broncode is. Je kunt ook make clean gebruiken om de eigenlijke object en binaire bestanden op te ruimen, maar als je van plan bent om je broncode te tarren of je code te distribueren naar de massa, kun je gewoon
cumin$ cd cmakecumin$ lsCMakeLists.txt build/ w01-cpp/cumin$ rm -rf build/cumin$ lsCMakeLists.txt w01-cpp/
om je code op te ruimen en te verpakken.
Dit raakt slechts de oppervlakte van wat CMake kan doen. Bekijk de CMake Wiki voor meer info. Ik hoop deze pagina bij te werken als we meer complexe voorbeelden tegenkomen, en ik neem graag feedback of tips aan van andere cursussen die CMake zouden kunnen gebruiken. Voor zeer gevorderde gebruikers heeft CMake een metgezel programma CPack voor het automatisch verpakken van binaries en broncode voor Ubuntu/Debian (.deb), Red Hat (.rpm), OSXX11, tgz, CygWin, en Nullsoft systemen.