Table of Contents · 2020-03-30 · work will save you hundreds of lines and hours of...

Post on 15-Apr-2020

4 views 0 download

Transcript of Table of Contents · 2020-03-30 · work will save you hundreds of lines and hours of...

1.1

1.1.1

1.1.2

1.1.3

1.1.4

1.2

1.2.1

1.2.2

1.2.3

1.2.4

1.2.5

1.2.6

1.3

1.3.1

1.3.2

1.3.3

1.3.4

1.3.5

1.3.6

1.4

1.4.1

1.4.2

1.4.3

1.5

1.5.1

1.5.2

1.6

1.6.1

1.6.2

1.6.3

1.7

1.7.1

1.7.2

1.7.3

1.7.4

1.7.5

1.7.5.1

1.7.5.2

1.7.5.3

TableofContentsAnIntroductiontoModernCMake

InstallingCMake

RunningCMake

DosandDon'ts

What'snewinCMake

IntroductiontotheBasics

VariablesandtheCache

ProgramminginCMake

Communicatingwithyourcode

HowtoStructureYourProject

RunningOtherPrograms

ASimpleExample

AddingFeatures

C++11andBeyond

Smallbutcommonneeds

Utilities

Usefulmodules

IDEs

Debugging

IncludingProjects

Submodule

DownloadProject

Fetch(CMake3.11)

Testing

GoogleTest

Catch

ExportingandInstalling

Installing

Exporting

Packaging

Lookingforlibraries

CUDA

OpenMP

Boost

MPI

ROOT

UseFileExample

SimpleExample

DictionaryExample

1

1.7.6Minuit2

2

AnIntroductiontoModernCMakePeoplelovetohatebuildsystems.JustwatchthetalksfromCppCon17toseeexamplesofdevelopersmakingthestateofbuildsystemsthebruntofjokes.Thisraisesthequestion:Why?Certainlytherearenoshortageofproblemswhenbuilding.ButIthinkthat,in2020,wehaveaverygoodsolutiontoquiteafewofthoseproblems.It'sCMake.NotCMake2.8though;thatwasreleasedbeforeC++11evenexisted!NorthehorribleexamplesoutthereforCMake(eventhosepostedonKitWare'sowntutorialslist).I'mtalkingaboutModernCMake.CMake3.1+,maybeevenCMake3.17+!It'sclean,powerful,andelegant,soyoucanspendmostofyourtimecoding,notaddinglinestoanunreadable,unmaintainableMake(OrCMake2)file.AndCMake3.11+issupposedtobesignificantlyfaster,aswell!

Thisbookismeanttobealivingdocument.YoucanraiseanissueorputinamergerequestonGitLab.YoucanalsodownloadacopyasaPDF.

Inshort,herearethemostlikelyquestionsinyourmindifyouareconsideringModernCMake:

WhydoIneedagoodbuildsystem?Doanyofthefollowingapplytoyou?

Youwanttoavoidhard-codingpathsYouneedtobuildapackageonmorethanonecomputerYouwanttouseCI(continuousintegration)YouneedtosupportdifferentOSs(maybeevenjustflavorsofUnix)YouwanttosupportmultiplecompilersYouwanttouseanIDE,butmaybenotallofthetimeYouwanttodescribehowyourprogramisstructuredlogically,notflagsandcommandsYouwanttousealibraryYouwanttousetools,likeClang-Tidy,tohelpyoucodeYouwanttouseadebugger

Ifso,you'llbenefitfromaCMake-likebuildsystem.

WhymusttheanswerbeCMake?Buildsystemsisahottopic.Ofcoursetherearemanyoptions.Butevenareallygoodone,oronethatre-usesafamiliarsyntax,can'tcomeclosetoCMake.Why?Support.EveryIDEsupportsCMake(orCMakesupportsthatIDE).MorepackagesuseCMakethananyothersystem.So,ifyouusealibrarythatisdesignedtobeincludedinyourcode,youhaveachoice:Makeyourownbuildsystem,oruseoneofoftheprovidedones,andthatwillalmostalwaysincludeCMake.Andthatwillquicklybethecommondenominatorifyouincludemultipleprojects.And,ifyouneedalibrarythat'spreinstalled,thechancesofithavingafindCMakescriptorconfigCMakescriptareexcellent.

WhyuseaModernCMake?AroundCMake2.6-2.8,CMakestartedtakingover.ItwasinmostofthepackagemanagersforLinuxOS's,andwasbeingusedinlotsofpackages.

ThenPython3cameout.

Iknow,thisshouldhavenothingwhatsoevertodowithCMake.

AnIntroductiontoModernCMake

3

Butithada3.Anditfollowed2.Anditwasahard,ugly,transitionthatisstillongoinginsomeplaces,eventoday.

IbelievethatCMake3hadthebadlucktofollowPython3. EventhougheveryversionofCMakeisinsanelybackwardcompatible,the3serieswastreatedasifitwassomethingnew.Andso,you'llfindOS'slikeCentOS7withGCC4.8,withalmost-completeC++14support,andCMake2.8,whichcameoutbeforeC++11.

YoureallyshouldatleastuseaversionofCMakethatcameoutafteryourcompiler,sinceitneedstoknowcompilerflags,etc,forthatversion.And,sinceCMakewilldumbitselfdowntotheminimumrequiredversioninyourCMakefile,installinganewCMake,evensystemwide,isprettysafe.Youshouldatleastinstallitlocally.It'seasy(1-2linesinmanycases),andyou'llfindthat5minutesofworkwillsaveyouhundredsoflinesandhoursofCMakeLists.txtwriting,andwillbemucheasiertomaintaininthelongrun.

Thisbooktriestosolvetheproblemofthepoorexamplesandbestpracticesthatyou'llfindproliferatingtheweb.

Othersources

Othermaterialfromtheoriginalauthorofthisbook:

CMakeWorkshopInteractiveModernCMaketalk

Therearesomeotherplacestofindgoodinformationontheweb.Herearesomeofthem:

Theofficialhelp:Reallyamazingdocumentation.Nicelyorganized,greatsearch,andyoucantoggleversionsatthetop.Itjustdoesn'thaveagreat"bestpracticestutorial",whichiswhatthisbooktriestofillin.EffectiveModernCMake:Agreatlistofdo'sanddon'ts.EmbracingModernCMake:ApostwithgooddescriptionofthetermIt'stimetodoCMakeRight:AnicesetofbestpracticesforModernCMakeprojects.TheUltimateGuidetoModernCMake:Aslightlydatedpostwithsimilarintent.MoreModernCMake:AgreatpresentationfromMeetingC++2018thatrecommendsCMake3.12+.ThistalkmakescallsCMake3.0+"ModernCMake"andCMake3.12+"MoreModernCMake".OhNo!MoreModernCMake:ThesequeltoMoreModernCMake.toeb/moderncmake:AnicepresentationandexamplesaboutCMake3.5+,withintrotosyntaxthroughprojectorganization

Credits

ModernCMakewasoriginallywrittenbyHenrySchreiner.OthercontributorscanbefoundlistedonGitLab.

.CMake3.0alsoremovedseverallongdeprecatedfeaturesfromveryoldversionsofCMakeandmakeoneverytinybackwardsincompatiblechangetosyntaxrelatedtosquarebrackets,sothisisnotentirelyfair;theremightbesomevery,veryoldCMakefilesthatwouldstopworkingwith3.I'veneverseenone,though.↩

1

1

AnIntroductiontoModernCMake

4

InstallingCMake

YourCMakeversionshouldbenewerthanyourcompiler.Itshouldbenewerthanthelibrariesyouareusing(especiallyBoost).Newversionsworkbetterforeveryone.

IfyouhaveabuiltincopyofCMake,itisn'tspecialorcustomizedforyoursystem.Youcaneasilyinstallanewoneinstead,eitheronthesystemlevelortheuserlevel.FeelfreetoinstructyourusershereiftheycomplainaboutaCMakerequirementbeingsettoohigh.Especiallyiftheywant<3.1support.MaybeeveniftheywantCMake<3.17support...

Quicklist(moreinfooneachmethodbelow)

Orderedbyauthorpreference:

AllPip(official,sometimesdelayedslightly)Anaconda/Conda-Forge

WindowsChocolateyScoopMSYS2Downloadbinary(official)

MacOSHomebrewMacPortsDownloadbinary(official)

LinuxSnapcraft(official)APTrepository(Ubuntu/Debianonly)(official)Downloadbinary(official)

OfficialpackageYoucandownloadCMakefromKitWare.ThisishowyouwillprobablygetCMakeifyouareonWindows.It'snotabadwaytogetitonmacOSeither,butusingbrewinstallcmakeismuchnicerifyouuseHomebrew(andyoushould).Youcanalsogetitonmostotherpackagemanagers,suchasChocolateyforWindowsorMacPortsformacOS.

OnLinux,thereareseveraloptions.KitwareprovidesaDebian/Ubunutuaptrepository,aswellassnappackages.ThereareuniversalLinuxbinariesprovided,butyou'llneedtopickaninstalllocation.Ifyoualreadyuse~/.localforuser-spacepackages,thefollowingsinglelinecommand willgetCMakeforyou :

IfyoujustwantalocalfolderwithCMakeonly:

1 2

~$wget-qO-"https://cmake.org/files/v3.17/cmake-3.17.0-Linux-x86_64.tar.gz"|tar--

strip-components=1-xz-C~/.local

~$mkdir-pcmake-3.17&&wget-qO-"https://cmake.org/files/v3.17/cmake-3.17.0-Linux-

x86_64.tar.gz"|tar--strip-components=1-xz-Ccmake-3.17

~$exportPATH=`pwd`/cmake-3.17/bin:$PATH

InstallingCMake

5

You'llobviouslywanttoappendtothePATHeverytimeyoustartanewterminal,oraddittoyour.bashrcortoanLModsystem.

And,ifyouwantasysteminstall,installto/usr/local;thisisanexcellentchoiceinaDockercontainer,forexampleonGitLabCI.Donottryitonanon-containerizedsystem.

Ifyouareonasystemwithoutwget,replacewget-qO-withcurl-s.

YoucanalsobuildCMakeonanysystem,it'sprettyeasy,butbinariesarefaster.

CMakeDefaultVersions

HerearesomecommonbuildenvironmentsandtheCMakeversionyou'llfindonthem.FeelfreetoinstallCMakeyourself,it's1-2linesandthere'snothing"special"aboutthebuiltinversion.It'salsoverybackwardcompatible.

Windows

AlsoScoopisgenerallyuptodate.ThenormalinstallersfromCMake.orgarecommononWindows,too.

MacOS

HomebrewisquiteabitmorepopularnowadaysonmacOS,atleastaccordingtoGoogleTrends.

Linux

RHEL/CentOS

Thedefaulton8isnottoobad,butyoushouldnotusethedefaulton7.UsetheEPELpackageinstead.

Ubuntu

YoushouldonlyusethedefaultCMakeon18.04+;it'sanLTSreleasewithaprettydecentminimumversion!

Other

Generaltools

docker$wget-qO-"https://cmake.org/files/v3.17/cmake-3.17.0-Linux-x86_64.tar.gz"|tar

--strip-components=1-xz-C/usr/local

InstallingCMake

6

InstallingCMake

8

Alsoseepkgs.org/download/cmake.

Pip

Thisisalsoprovidedasanofficialpackage,maintainedbytheauthorsofCMakeatKitWare.It'sarathernewmethod,andmightfailonsomesystems(Alpineisn'tsupportedlastIchecked,butthathasCMake3.8),butworksreallywellwhenitworks(likeonTravisCI).Ifyouhavepip(Python'spackageinstaller),youcando:

Andaslongasabinaryexistsforyoursystem,you'llbeup-and-runningalmostimmediately.Ifabinarydoesn'texist,itwilltrytouseKitWare'sscikit-buildpackagetobuild,whichcurrentlycan'tbelistedasadependencyinthepackagingsystem,andmightevenrequire(anolder)copyofCMaketobuild.Soonlyusethissystemifbinariesexist,whichismostofthetime.

Thishasthebenefitofrespectingyourcurrentvirtualenvironment,aswell.

Personally,onLinux,IputversionsofCMakeinfolders,like/opt/cmake312or~/opt/cmake312,andthenaddthemto[LMod].Seeenvmodule_setupforhelpsettingupanLModsystemonmacOSorLinux.Ittakesabittolearn,butisagreatwaytomanagepackageandcompilerversions.

.Iassumethisisobvious,butyouaredownloadingandrunningcode,whichexposesyoutoamaninthemiddleattack.Ifyouareinacriticalenvironment,youshoulddownloadthefileandcheckthechecksum.(And,no,simplydoingthisintwostepsdoesnotmakeyouanysafer,onlyachecksumissafer).↩

.Ifyoudon'thavea.localinyourhomedirectory,it'seasytostart.Justmakethefolder,thenaddexportPATH="$HOME/.local/bin:$PATH"toyour.bashrcor.bash_profileor.profilefileinyourhomedirectory.Nowyoucaninstallanypackagesyoubuildto-DCMAKE_INSTALL_PREFIX=~/.localinsteadof/usr/local!↩

gitbook$pipinstallcmake

1

2

InstallingCMake

9

RunningCMakeBeforewritingCMake,let'smakesureyouknowhowtorunittomakethings.ThisistrueforalmostallCMakeprojects,whichisalmosteverything.

Buildingaproject

Unlessotherwisenoted,youshouldalwaysmakeabuilddirectoryandbuildfromthere.Youcantechnicallydoanin-sourcebuild,butyou'llhavetobecarefulnottooverwritefilesoraddthemtogit,sojustdon't.

Here'stheClassicCMakeBuildProcedure(TM):

Youcanreplacethemakelinewithcmake--build.ifyou'dlike,anditwillcallmakeorwhateverbuildtoolyouareusing.IfyouareusinganewerversionofCMake(whichyouusuallyshouldbe,exceptforcheckingcompatibilitywitholderCMake),youcaninsteaddothis:

Anyoneofthesecommandswillinstall:

Sosetofmethodsshouldyouuse?Aslongasyoudonotforgettotypethebuilddirectoryastheargument,stayingoutofthebuilddirectoryisshorter,andmakingsourcechangesiseasierfromthesourcedirectory.Youshouldtrytogetusedtousing--build,asthatwillfreeyoufromusingonlymaketobuild.Notethatworkingfromthebuilddirectoryishistoricallymuchmorecommon,andsometoolsandcommands(includingCTest)stillrequirerunningfromthebuilddirectory.

Justtoclarify,youcanpointCMakeateitherthesourcedirectoryfromthebuilddirectory,oratanexistingbuilddirectoryfromanywhere.

Ifyouusecmake--buildinsteadofdirectlycallingtheunderlyingbuildsystem,youcanuse-vforverbosebuilds(CMake3.14+),-jNforparallelbuildsonNcores(CMake3.12+),and--target(anyversionofCMake)or-t(CMake3.15+)topickatarget.Otherwise,thesecommandsvarybetweenbuildsystems,suchasVERBOSE=1makeandninja-v.

Pickingacompiler

~/package$mkdirbuild

~/package$cdbuild

~/package/build$cmake..

~/package/build$make

~/package$cmake-S.-Bbuild

~/package$cmake--buildbuild

#Fromthebuilddirectory(pickone)

~/package/build$makeinstall

~/package/build$cmake--build.--targetinstall

~/package/build$cmake--install.#CMake3.15+only

#Fromthesourcedirectory(pickone)

~/package$cmake--buildbuild--targetinstall

~/package$cmake--installbuild#CMake3.15+only

RunningCMake

10

Selectingacompilermustbedoneonthefirstruninanemptydirectory.It'snotCMakesyntaxperse,butyoumightnotbefamiliarwithit.TopickClang:

ThatsetstheenvironmentvariablesinbashforCCandCXX,andCMakewillrespectthosevariables.Thissetsitjustforthatoneline,butthat'stheonlytimeyou'llneedthose;afterwardsCMakecontinuestousethepathsitdeducesfromthosevalues.

PickingageneratorYoucanbuildwithavarietyoftools;makeisusuallythedefault.ToseeallthetoolsCMakeknowsaboutonyoursystem,run

Andyoucanpickatoolwith-G"MyTool"(quotesonlyneededifspacesareinthetoolname).YoushouldpickatoolonyourfirstCMakecallinadirectory,justlikethecompiler.Feelfreetohaveseveralbuilddirectories,likebuild/andbuildXcode.YoucansettheenvironmentvariableCMAKE_GENERATORtocontrolthedefaultgenerator(CMake3.15+).Notethatmakefileswillonlyruninparallelifyouexpliciltypassanumberofthreads,suchasmake-j2,whileNinjawillautomaticallyruninparallel.Youcandirectlypassaparallelizationoptionsuchas-j2tothecmake--build.commandinrecentversionsofCMake.

Settingoptions

YousetoptionsinCMakewith-D.Youcanseealistofoptionswith-L,oralistwithhuman-readablehelpwith-LH.Ifyoudon'tlistthesource/builddirectory,thelistingwillnotrerunCMake(cmake-Linsteadofcmake-L.).

Verboseandpartialbuilds

Again,notreallyCMake,butifyouareusingacommandlinebuildtoollikemake,youcangetverbosebuilds:

YoucanactuallywritemakeVERBOSE=1,andmakewillalsodotherightthing,thoughthat'safeatureofmakeandnotthecommandlineingeneral.

Youcanalsobuildjustapartofabuildbyspecifyingatarget,suchasthenameofalibraryorexecutableyou'vedefinedinCMake,andmakewilljustbuildthattarget.

Options

CMakehassupportforcachedoptions.AVariableinCMakecanbemarkedas"cached",whichmeansitwillbewrittentothecache(afilecalledCMakeCache.txtinthebuilddirectory)whenitisencountered.Youcanpreset(orchange)thevalueofacachedoptiononthecommandlinewith-D.WhenCMakelooksforacachedvariable,itwillusetheexistingvalueandwillnotoverwriteit.

Standardoptions

ThesearecommonCMakeoptionstomostpackages:

-DCMAKE_BUILD_TYPE=PickfromRelease,RelWithDebInfo,Debug,orsometimesmore.-DCMAKE_INSTALL_PREFIX=Thelocationtoinstallto.SysteminstallonUNIXwouldoftenbe/usr/local(thedefault),userdirectoriesareoften~/.local,oryoucanpickafolder.

~/package/build$CC=clangCXX=clang++cmake..

~/package/build$cmake--help

~/package/build$VERBOSE=1make

RunningCMake

11

-DBUILD_SHARED_LIBS=YoucansetthisONorOFFtocontrolthedefaultforsharedlibraries(theauthorcanpickonevs.theotherexplicitlyinsteadofusingthedefault,though)-DBUILD_TESTING=Thisisacommonnameforenablingtests,notallpackagesuseit,though,sometimeswithgoodreason.

DebuggingyourCMakefiles

We'vealreadymentionedverboseoutputforthebuild,butyoucanalsoseeverboseCMakeconfigureoutputtoo.The--traceoptionwillprinteverylineofCMakethatisrun.Sincethisisveryverbose,CMake3.7added--trace-source="filename",whichwillprintouteveryexecutedlineofjustthefileyouareinterestedinwhenitruns.Ifyouselectthenameofthefileyouareinterestedindebugging(usuallybyselectingtheparentdirectorywhendebuggingaCMakeLists.txt,sinceallofthosehavethesamename),youcanjustseethelinesthatruninthatfile.Veryuseful!

RunningCMake

12

Do'sandDon'ts

CMakeAntipatterns

ThenexttwolistsareheavilybasedontheexcellentgistEffectiveModernCMake.Thatlistismuchlongerandmoredetailed,feelfreetoreaditaswell.

Donotuseglobalfunctions:Thisincludeslink_directories,include_libraries,andsimilar.Don'taddunneededPUBLICrequirements:Youshouldavoidforcingsomethingonusersthatisnotrequired(-Wall).MakethesePRIVATEinstead.Don'tGLOBfiles:MakeoranothertoolwillnotknowifyouaddfileswithoutrerunningCMake.NotethatCMake3.12addsaCONFIGURE_DEPENDSflagthatmakesthisfarbetterifyouneedtouseit.Linktobuiltfilesdirectly:Alwayslinktotargetsifavailable.NeverskipPUBLIC/PRIVATEwhenlinking:Thiscausesallfuturelinkingtobekeyword-less.

CMakePatternsTreatCMakeascode:Itiscode.Itshouldbeascleanandreadableasallothercode.Thinkintargets:Yourtargetsshouldrepresentconcepts.Makean(IMPORTED)INTERFACEtargetforanythingthatshouldstaytogetherandlinktothat.Exportyourinterface:Youshouldbeabletorunfrombuildorinstall.WriteaConfig.cmakefile:Thisiswhatalibraryauthorshoulddotosupportclients.MakeALIAStargetstokeepusageconsistent:Usingadd_subdirectoryandfind_packageshouldprovidethesametargetsandnamespaces.Combinecommonfunctionalityintoclearlydocumentedfunctionsormacros:Functionsarebetterusually.Uselowercasefunctionnames:CMakefunctionsandmacroscanbecalledloweroruppercase.Alwaysuserlowercase.Uppercaseisforvariables.Usecmake_policyand/orrangeofversions:Policieschangeforareason.OnlypiecemealsetOLDpoliciesifyouhaveto.

DosandDon'ts

13

What'snewininCMakeThisisanabbreviatedversionoftheCMakechanglogwithjustthehighlightsforauthors.Namesforeachreleasearearbitrarilypickedbytheauthor.

CMake3.0:Interfacelibraries

TherewereatonofadditionstothisversionofCMake,primarilytofilloutthetargetinterface.SomebitsofneededfunctionalityweremissedandimplementedinCMake3.1instead.

NewdocumentationINTERFACElibrariesProjectVERSIONsupportExportingbuildtreeseasilyBracketargumentsandcommentsavailable(notwidelyused)Lotsofimprovements

CMake3.1:C++11andcompilefeatures

ThisisthefirstreleaseofCMaketosupportC++11.CombinedwithfixestothenewfeaturesofCMake3.0,thisiscurrentlyacommonminimumversionofCMakeforlibrariesthatwanttosupportoldCMakebuilds.

C++11SupportCompilefeaturessupportSourcescanbeaddedlaterwithtarget_sourcesBettersupportforgeneratorexpressionsandINTERFACEtargets

CMake3.2:UTF8Thisisasmallerrelease,withmostlysmallfeaturesandfixes.Internalchanges,likebetterWindowsandUTF8support,werethefocus.

continue()insideloopsFileanddirectorylocksadded

CMake3.3:ifIN_LIST

ThisisnotablefortheusefulIN_LISToptionforif,butitalsoaddedbetterlibrarysearchusing$PATH(SeeCMake3.6),dependenciesforINTERFACElibraries,andseveralotherusefulimprovements.TheadditionofaCOMPILE_LANGUAGEgeneratorexpressionwouldproveveryusefulinthefutureasmorelanguagesareadded.Makefilesnowproducebetteroutputinparallel.

IN_LISTaddedtoif*_INCLUDE_WHAT_YOU_USEpropertyaddedCOMPILE_LANGUAGEgeneratorexpression(limitedsupportinsomegenerators)

CMake3.4:Swift&CCacheThisreleaseaddslotsofusefultools,supportfortheSwiftlanguage,andtheusualimprovements.Italsostartedsupportingcompilerlaunchers,likeCCache.

AddedSwiftlanguage

What'snewinCMake

14

AddedBASE_DIRtoget_filename_componentif(TEST...)addedstring(APPEND...)addedCMAKE_*_COMPILER_LAUNCHERaddedformakeandninjaTARGET_MESSAGESallowmakefilestoprintmessagesaftertargetiscompletedImportedtargetsarebeginningtoshowupintheofficialFind*.cmakefiles

CMake3.5:ARM

ThisreleaseexpandedCMaketomoreplatforms,andmakewarningseasiertocontrolfromthecommandline.

Multipleinputfilessupportedforseveralofthecmake-Ecommands.cmake_parse_argumentsnowbuiltinBoost,GTest,andmorenowsupportimportedtargetsARMCCnowsupported,bettersupportforiOSXCodebackslashfix

CMake3.6:Clang-Tidy

ThisreleaseaddedClang-Tidysupport,alongwithmoreutilitiesandimprovements.Italsoremovedthesearchof$PATHonUnixsystemsduetoproblems,insteadusersshoulduse$CMAKE_PREFIX_PATH.

EXCLUDE_FROM_ALLforinstalllist(FILTERaddedCMAKE_*_STANDARD_INCLUDE_DIRECTORIESandCMAKE_*_STANDARD_LIBRARIESaddedfortoolchainsTry-compileimprovements*_CLANG_TIDYpropertyaddedExternalprojectscannowbeshallowclones,andotherimprovements

CMake3.7:Android&CMakeServerYoucannowcross-compiletoAndroid.Usefulnewifstatementoptionsreallyhelpclarifycode.AndthenewservermodewassupposedtoimproveintegrationwithIDEs(butisbeingreplacedbyadifferentsysteminCMake3.14+).SupportfortheVIMeditorwasalsoimproved.

PARSE_ARGVmodeforcmake_parse_argumentsBetter32-bitsupporton64-bitmachinesLotsofusefulnewifcomparisons,likeVERSION_GREATER_EQUAL(really,whydidittakethislong?)LINK_WHAT_YOU_USEaddedLotsofcustompropertiesrelatedtofilesanddirectoriesCMakeServeraddedAdded--trace-source="filename"tomonitorcertainfilesonly

CMake3.8:C#&CUDA

ThisaddsCUDAasalanguage,aswellascxx_std_11asacompilermeta-feature.ThenewgeneratorexpressioncouldbereallyusefulifyoucanrequireCMake3.8+!

NativesupportforC#asalanguageNativesupportforCUDAasalanguageMetafeaturescxx_std_11(and14,17)addedtry_compilehasbetterlanguagesupport

What'snewinCMake

15

BUILD_RPATHpropertyaddedCOMPILE_FLAGSnowsupportsgeneratorexpression*_CPPLINTadded$<IF:cond,true-value,false-value>added(wow!)source_group(TREEadded(finallyallowingIDE'storeflecttheprojectfolderstructure!)

CMake3.9:IPO

LotsoffixestoCUDAsupportwentintothisrelease,includingPTXsupportandMSVCgenerators.InterproceduralOptimizationsarenowsupportedproperly.Evenmoremodulesprovideimportedtargets,includingMPI.

CUDAsupportedforWindowsBetterobjectlibrarysupportinseveralsituationsDESCRIPTIONaddedtoprojectseparate_argumentsgetsNATIVE_COMMANDINTERPROCEDURAL_OPTIMIZATIONenforced(andCMAKE_*initializeradded,CheckIPOSupportedadded,ClangandGCCsupport)NewGoogleTestmoduleFindDoxygendrasticallyimproved

CMake3.10:CppCheck

CMakenowisbuiltwithC++11compilers.Lotsofusefulimprovementshelpwritecleanercode.

SupportforflangFortrancompilerCompilerlauncheraddedtoCUDAIndented#cmakedefinesnowsupportedforconfigure_fileinclude_guard()addedtoensureafilegetsincludedonlyoncestring(PREPENDadded*_CPPCHECKpropertyaddedLABELSaddedtodirectoriesFindMPIvastlyexpandedFindOpenMPimprovedDynamictestdiscoveryforGoogleTest

CMake3.11:Faster&IMPORTEDINTERFACEThisreleaseissupposedtobemuchfaster.YoucanalsofinallydirectlyaddINTERFACEtargetstoIMPORTEDlibraries(theinternalFind*.cmakescriptsshouldbecomemuchcleanereventually).

FortransupportscompilerlaunchersXcodeandVisualStudiosupportCOMPILE_LANGUAGEgeneratorexpressionsfinallyYoucannowaddINTERFACEtargetsdirectlytoIMPORTEDINTERFACElibraries(Wow!)SourcefilepropertieshavebeenexpandedFetchContentmodulenowallowsdownloadstohappenatconfiguretime(Wow)

CMake3.12:VersionrangesandCONFIGURE_DEPENDS

Verypowerfulrelease,containinglotsofsmallerlong-requestedfeatures.Oneofthesmallerbutimmediatelynoticeablechangesistheadditionofversionranges;youcannowsetboththeminimumandmaximumknownCMakeversioneasily.YoucanalsosetCONFIGURE_DEPENDSonaGLOBedsetoffiles,andthebuildsystemwillcheckthosefilesandrerunifneeded!YoucanusethegeneralPackageName_ROOTforallfind_packagesearches.Lotsofadditionstostringsandlists,moduleupdates,shinynewPythonfindmodule(2and3versionstoo),andmanymore.

What'snewinCMake

16

Supportforcmake_minimum_requiredranges(backwardcompatible)Supportfor-j,--parallelin--buildmode(passedontobuildtool)SupportforSHELL:stringsincompileoptions(notdeduplicated)NewFindPythonmodulestring(JOINandlist(JOIN,andlist(TRANSFORMfile(TOUCHandfile(GLOBCONFIGURE_DEPENDSC++20supportCUDAasalanguageimprovements:CUDA7and7.5nowsupportedSupportforOpenMPonmacOS(commandlineonly)SeveralnewpropertiesandpropertyinitializersCPackfinallyreadsCMAKE_PROJECT_VERSIONvariables

CMake3.13:Linkingcontrol

YoucannowmakesymboliclinksonWindows!LotsofnewfunctionsthatfilloutthepopularrequestsforCMake,suchasadd_link_options,target_link_directories,andtarget_link_options.Youcannowdoquiteabitmoremodificationtotargetsoutsideofthesourcedirectory,forbetterfileseparation.And,target_sourcesfinallyhandlesrelativepathsproperly(policy76).

Newctest--progressoptionforliveoutputtarget_link_optionsandadd_link_optionsaddedtarget_link_directoriesaddedSymboliclinkcreation,-Ecreate_symlink,supportedonWindowsIPOsupportedonWindowsYoucanuse-Sand-Bforsourceandbuilddirectoriestarget_link_librariesandinstallworkoutsidethecurrenttargetdirectorySTATIC_LIBRARY_OPTIONSpropertyaddedtarget_sourcesisnowrelativetothecurrentsourcedirectory(CMP0076)IfyouuseXcode,younowcanexperimentallysetschemafields

CMake3.14:Fileutilities(AKACMakeπ)

Thisreleasehaslotsofsmallcleanups,includingseveralutilitiesforfiles.Generatorexpressionsworkinafewmoreplaces,andlisthandlingisbetterwithemptyvariables.Quiteafewmorefindpackagesproducetargets.ThenewVisualStudio162019generatorisabitdifferentthanolderversions.WindowsXPandVistasupporthasbeendropped.

Thecmake--buildcommandgained-v/--verbose,touseverbosebuildsifyourbuildtoolsupportsitTheFILEcommandgainedCREATE_LINK,READ_SYMLINK,andSIZEget_filename_componentgainedLAST_EXTandNAME_WLEtoaccessjustthelastextensiononafile,whichwouldget.ziponafilesuchasversion.1.2.zip(veryhandy!)YoucanseeifavariableisdefinedintheCACHEwithDEFINEDCACHE{VAR}inanifstatement.BUILD_RPATH_USE_ORIGINandCMakeversionwereaddedtoimprovehandlingofRPathinthebuilddirectory.TheCMakeservermodeisnowbeingreplacedwithafileAPI,startinginthisrelease.WillaffectIDEsinthelongrun.

CMake3.15:CLIupgradeThisreleasehasmanysmallerpolishingchanges,includeseveralofimprovementstotheCMakecommandline,suchascontroloverthedefaultgeneratorthroughenvironmentvariables(sonowit'seasytochangethedefaultgeneratortoNinja).Multipletargetsaresupportedin--buildmode,and--installmodeadded.CMakefinallysupportsmultiplelevelsoflogging.Generatorexpressionsgainedafewhandytools.ThestillverynewFindPythonmodulecontinuestoimprove,andFindBoostisnowmoreinlinewithBoost1.70'snewCONFIGmodule.export(PACKAGE)hasdrasticallychanged;itnownolongertouches$HOME/.cmakebydefault(ifCMakeMinimumversionis3.15orhigher),andrequiresanextrastepifauserwantstouseit.Thisisgenerallylesssurprising.

What'snewinCMake

17

CMAKE_GENERATORenvironmentvariableaddedtocontroldefaultgeneratorMultipletargetsupportinbuildmode,cmake.--build--targetabShortcut-tfor--targetInstallsupport,cmake.--install,doesnotinvokethebuildsystemSupportfor--loglevelandNOTICE,VERBOSE,DEBUG,andTRACEformessageThelistcommandgainedPREPEND,POP_FRONT,andPOP_BACKexecute_processgainedCOMMAND_ECHOoption(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO)allowsyoutoautomaticallyechocommandsbeforerunningthemSeveralNinjaimprovements,includeSWIFTlanguagesupportCompilerandlistimprovementstogeneratorexpressions

CMake3.16:Unitybuilds

Anewunitybuildmodewasadded,allowingsourcefilestobemergedintoasinglebuildfile.Supportforprecompiledheaders(possiblypreparingforC++20modules,perhaps?)wasadded.Lotsofothersmallerfixeswereimplemented,especiallytonewerfeatures,suchastoFindPython,FindDoxygen,andothers.

AddedsupportforObjectiveCandObjectiveC++languagesSupportforprecompilingheaders,withtarget_precompile_headersSupportfor"Unity"or"Jumbo"builds(mergingsourcefiles)withCMAKE_UNITY_BUILDCTest:Cannowskipbasedonregex,expandlistsSeveralnewfeaturestocontrolRPath.Generatorexpressionsworkinmoreplaces,likebuildandinstallpathsFindlocationscannowbeexplicitlycontrolledthroughnewvariables

CMake3.17:MoreCUDA

AFindCUDAToolkitwasfinallyadded,whichallowsfindingandusingtheCUDAtoolkitwithoutenablingtheCUDAlanguage!CUDAnowisabitmoreconfigurable,suchaslinkingtosharedlibraries.Quiteabitmorepolishintheexpectedareas,aswell,likeFindPython.Finally,youcannowiterateovermultiplelistsatatime.

CUDA_RUNTIME_LIBRARYcanfinallybesettoShared!FindCUDAToolkitfinallyaddedcmake-ErmreplacesolderremovecommandsCUDAhasmetafeatureslikecuda_std_03,etc.Youcantrackthesearchesforpackageswith--debug-findExternalProjectcannowdisablerecursivecheckoutsFindPythonbetterintegrationwithCondaDEPRECATIONcanbeappliedtotargetsCMakegainedarmcommandSeveralnewenvironmentvariablesforeachcannowdoZIP_LISTS(multiplelistsatatime)

What'snewinCMake

18

Introductiontothebasics

MinimumVersion

Here'sthefirstlineofeveryCMakeLists.txt,whichistherequirednameofthefileCMakelooksfor:

cmake_minimum_required(VERSION3.1)

Let'smentionabitofCMakesyntax.Thecommandnamecmake_minimum_requirediscaseinsensitive,sothecommonpracticeistouselowercase. TheVERSIONisaspecialkeywordforthisfunction.Andthevalueoftheversionfollowsthekeyword.Likeeverywhereinthisbook,justclickonthecommandnametoseetheofficialdocumentation,andusethedropdowntoswitchdocumentationbetweenCMakeversions.

Thislineisspecial! TheversionofCMakewillalsodictatethepolicies,whichdefinebehaviorchanges.So,ifyousetminimum_requiredtoVERSION2.8,you'llgetthewronglinkingbehavioronmacOS,forexample,eveninthenewestCMakeversions.Ifyousetitto3.3orless,you'llgetthewronghiddensymbolsbehaviour,etc.Alistofpoliciesandversionsisavailableatpolicies.

InCMake3.12,thiswillsupportarange,suchasVERSION3.1...3.12;thismeansyousupportaslowas3.1buthavealsotesteditwiththenewpolicysettingsupto3.12.Thisismuchniceronusersthatneedthebettersettings,andduetoatrickinthesyntax,it'sbackwardcompatiblewitholderversionsofCMake(thoughactuallyrunningCMake3.2-3.11willonlysetthe3.1versionofthepoliciesinthisexample).NewversionsofpoliciestendtobemostimportantformacOSandWindowsusers,whoalsousuallyhaveaveryrecentversionofCMake.

Thisiswhatnewprojectsshoulddo:

cmake_minimum_required(VERSION3.1...3.15)

if(${CMAKE_VERSION}VERSION_LESS3.12)

cmake_policy(VERSION${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})

endif()

IfCMakeversionislessthan3.12,theifblockwillbetrue,andthepolicywillbesettothecurrentCMakeversion.IfCMakeis3.12orhigher,theifblockwillbefalse,butthenewsyntaxincmake_minimum_requiredwillberespectedandthiswillcontinuetoworkproperly!

WARNING:MSVC'sCMakeservermodeoriginallyhadabuginreadingthisformat,soifyouneedtosupportnon-commandlineWindowsbuildsforolderMSVCversions,youwillwanttodothisinstead:

cmake_minimum_required(VERSION3.1)

if(${CMAKE_VERSION}VERSION_LESS3.15)

cmake_policy(VERSION${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})

else()

cmake_policy(VERSION3.15)

endif()

Ifyoureallyneedtosettoalowvaluehere,youcanusecmake_policytoconditionallyincreasethepolicylevelorsetaspecificpolicy.PleaseatleastdothisforyourmacOSusers!

Settingaproject

Now,everytop-levelCMakefilewillhavethenextline:

1

2

IntroductiontotheBasics

19

project(MyProjectVERSION1.0

DESCRIPTION"Veryniceproject"

LANGUAGESCXX)

Nowweseeevenmoresyntax.Stringsarequoted,whitespacedoesn'tmatter,andthenameoftheprojectisthefirstargument(positional).Allthekeywordargumentshereareoptional.Theversionsetsabunchofvariables,likeMyProject_VERSIONandPROJECT_VERSION.ThelanguagesareC,CXX,Fortran,ASM,CUDA(CMake3.8+),CSharp(3.8+),andSWIFT(CMake3.15+experimental).CCXXisthedefault.InCMake3.9,DESCRIPTIONwasaddedtosetaprojectdescription,aswell.Thedocumentationforprojectmaybehelpful.

Youcanaddcommentswiththe#character.CMakedoeshaveaninlinesyntaxforcommentstoo,butitisrarelyneeded,aswhitespacedoesn'tmatter.

There'sreallynothingspecialabouttheprojectname.Notargetsareaddedatthispoint.

Makinganexecutable

Althoughlibrariesaremuchmoreinteresting,andwe'llspendmostofourtimewiththem,let'sstartwithasimpleexecutable.

add_executable(onetwo.cppthree.h)

Thereareseveralthingstounpackhere.oneisboththenameoftheexecutablefilegenerated,andthenameoftheCMaketargetcreated(you'llhearalotmoreabouttargetssoon,Ipromise).Thesourcefilelistcomesnext,andyoucanlistasmanyasyou'dlike.CMakeissmart,andwillonlycompilesourcefileextensions.Theheaderswillbe,formostintentsandpurposes,ignored;theonlyreasontolistthemistogetthemtoshowupinIDEs.TargetsshowupasfoldersinmanyIDEs.Moreaboutthegeneralbuildsystemandtargetsisavailableatbuildsystem.

MakingalibraryMakingalibraryisdonewithadd_library,andisjustaboutassimple:

add_library(oneSTATICtwo.cppthree.h)

Yougettopickatypeoflibrary,STATIC,SHARED,orMODULE.Ifyouleavethischoiceoff,thevalueofBUILD_SHARED_LIBSwillbeusedtopickbetweenSTATICandSHARED.

Asyou'llseeinthefollowingsections,oftenyou'llneedtomakeafictionaltarget,thatis,onewherenothingneedstobecompiled,forexample,foraheader-onlylibrary.ThatiscalledanINTERFACElibrary,andisanotherchoice;theonlydifferenceisitcannotbefollowedbyfilenames.

YoucanalsomakeanALIASlibrarywithanexistinglibrary,whichsimplygivesyouanewnameforatarget.Theonebenefittothisisthatyoucanmakelibrarieswith::inthename(whichyou'llseelater).

Targetsareyourfriend

Nowwe'vespecifiedatarget,howdoweaddinformationaboutit?Forexample,maybeitneedsanincludedirectory:

target_include_directories(onePUBLICinclude)

3

IntroductiontotheBasics

20

target_include_directoriesaddsanincludedirectorytoatarget.PUBLICdoesn'tmeanmuchforanexecutable;foralibraryitletsCMakeknowthatanytargetsthatlinktothistargetmustalsoneedthatincludedirectory.OtheroptionsarePRIVATE(onlyaffectthecurrenttarget,notdependencies),andINTERFACE(onlyneededfordependencies).

Wecanthenchaintargets:

add_library(anotherSTATICanother.cppanother.h)

target_link_libraries(anotherPUBLICone)

target_link_librariesisprobablythemostusefulandconfusingcommandinCMake.Ittakesatarget(another)andaddsadependencyifatargetisgiven.Ifnotargetofthatname(one)exists,thenitaddsalinktoalibrarycalledoneonyourpath(hencethenameofthecommand).Oryoucangiveitafullpathtoalibrary.Oralinkerflag.Justtoaddafinalbitofconfusion,classicCMakeallowedyoutoskipthekeywordselectionofPUBLIC,etc.Ifthiswasdoneonatarget,you'llgetanerrorifyoutrytomixstylesfurtherdownthechain.

Focusonusingtargetseverywhere,andkeywordseverywhere,andyou'llbefine.

Targetscanhaveincludedirectories,linkedlibraries(orlinkedtargets),compileoptions,compiledefinitions,compilefeatures(seetheC++11chapter),andmore.Asyou'llseeinthetwoincludingprojectschapters,youcanoftengettargets(andalwaysmaketargets)torepresentallthelibrariesyouuse.Eventhingsthatarenottruelibraries,likeOpenMP,canberepresentedwithtargets.ThisiswhyModernCMakeisgreat!

DiveinSeeifyoucanfollowthefollowingfile.ItmakesasimpleC++11libraryandaprogramusingit.Nodependencies.I'lldiscussmoreC++standardoptionslater,usingtheCMake3.8systemfornow.

cmake_minimum_required(VERSION3.8)

project(CalculatorLANGUAGESCXX)

add_library(calclibSTATICsrc/calclib.cppinclude/calc/lib.hpp)

target_include_directories(calclibPUBLICinclude)

target_compile_features(calclibPUBLICcxx_std_11)

add_executable(calcapps/calc.cpp)

target_link_libraries(calcPUBLICcalclib)

.Inthisbook,I'llmostlyavoidshowingyouthewrongwaytodothings;youcanfindplentyofexamplesofthatonline.I'llmentionalternativesoccasionally,butthesearenotrecommendedunlesstheyareabsolutelynecessary;oftentheyarejusttheretohelpyoureadolderCMakecode.↩

.YouwillsometimesseeFATAL_ERRORhere,thatwasneededtosupportnicefailureswhenrunningthisinCMake<2.6,whichshouldnotbeaproblemanymore.↩

.The::syntaxwasoriginallyintendedforINTERFACEIMPORTEDlibraries,whichwereexplicitlysupposedtobelibrariesdefinedoutsidethecurrentproject.But,becauseofthis,mostofthetarget_*commandsdon'tworkonIMPORTEDlibraries,makingthemhardtosetupyourself.Sodon'tusetheIMPORTEDkeywordfornow,anduseanALIAStargetinstead;itwillbefineuntilyoustartexportingtargets.ThislimitationwasfixedinCMake3.11.↩

1

2

3

IntroductiontotheBasics

21

VariablesandtheCache

LocalVariables

Wewillcovervariablesfirst.Alocalvariableissetlikethis:

set(MY_VARIABLE"value")

Thenamesofvariablesareusuallyallcaps,andthevaluefollows.Youaccessavariablebyusing${},suchas${MY_VARIABLE}.CMakehastheconceptofscope;youcanaccessthevalueofthevariableafteryousetitaslongasyouareinthesamescope.Ifyouleaveafunctionorafileinasubdirectory,thevariablewillnolongerbedefined.YoucansetavariableinthescopeimmediatelyaboveyourcurrentonewithPARENT_SCOPEattheend.

Listsaresimplyaseriesofvalueswhenyousetthem:

set(MY_LIST"one""two")

whichinternallybecome;separatedvalues.Sothisisanidenticalstatement:

set(MY_LIST"one;two")

Thelist(commandhasutilitiesforworkingwithlists,andseparate_argumentswillturnaspaceseparatedstringintoalist(inplace).NotethatanunquotedvalueinCMakeisthesameasaquotedoneiftherearenospacesinit;thisallowsyoutoskipthequotesmostofthetimewhenworkingwithvaluethatyouknowcouldnotcontainspaces.

Whenavariableisexpandedusing${}syntax,allthesamerulesaboutspacesapply.Beespeciallycarefulwithpaths;pathsmaycontainaspaceatanytimeandshouldalwaysbequotedwhentheyareavariable(neverwrite${MY_PATH},alwaysshouldbe"${MY_PATH}").

CacheVariables

Ifyouwanttosetavariablefromthecommandline,CMakeoffersavariablecache.Somevariablesarealreadyhere,likeCMAKE_BUILD_TYPE.Thesyntaxfordeclaringavariableandsettingitifitisnotalreadysetis:

set(MY_CACHE_VARIABLE"VALUE"CACHESTRING"Description")

Thiswillnotreplaceanexistingvalue.ThisissothatyoucansettheseonthecommandlineandnothavethemoverriddenwhentheCMakefileexecutes.Ifyouwanttousethesevariablesasamake-shiftglobalvariable,thenyoucando:

set(MY_CACHE_VARIABLE"VALUE"CACHESTRING""FORCE)

mark_as_advanced(MY_CACHE_VARIABLE)

Thefirstlinewillcausethevaluetobesetnomatterwhat,andthesecondlinewillkeepthevariablefromshowingupinthelistofvariablesifyouruncmake-L..oruseaGUI.Thisissocommon,youcanalsousetheINTERNALtypetodothesamething(thoughtechnicallyitforcestheSTRINGtype,thiswon'taffectanyCMakecodethatdependsonthevariable):

set(MY_CACHE_VARIABLE"VALUE"CACHEINTERNAL"")

SinceBOOLissuchacommonvariabletype,youcansetitmoresuccinctlywiththeshortcut:

option(MY_OPTION"Thisissettablefromthecommandline"OFF)

1

VariablesandtheCache

22

FortheBOOLdatatype,thereareseveraldifferentwordingsforONandOFF.

Seecmake-variablesforalistingofknownvariablesinCMake.

Environmentvariables

Youcanalsoset(ENV{variable_name}value)andget$ENV{variable_name}environmentvariables,thoughitisgenerallyaverygoodideatoavoidthem.

TheCache

Thecacheisactuallyjustatextfile,CMakeCache.txt,thatgetscreatedinthebuilddirectorywhenyourunCMake.ThisishowCMakeremembersanythingyouset,soyoudon'thavetore-listyouroptionseverytimeyourerunCMake.

Properties

TheotherwayCMakestoresinformationisinproperties.Thisislikeavariable,butitisattachedtosomeotheritem,likeadirectoryoratarget.Aglobalpropertycanbeausefuluncachedglobalvariable.ManytargetpropertiesareinitializedfromamatchingvariablewithCMAKE_atthefront.SosettingCMAKE_CXX_STANDARD,forexample,willmeanthatallnewtargetscreatedwillhaveCXX_STANDARDsettothatwhentheyarecreated.Therearetwowaystosetproperties:

set_property(TARGETTargetName

PROPERTYCXX_STANDARD11)

set_target_properties(TargetNamePROPERTIES

CXX_STANDARD11)

Thefirstformismoregeneral,andcansetmultipletargets/files/testsatonce,andhasusefuloptions.Thesecondisashortcutforsettingseveralpropertiesononetarget.Andyoucangetpropertiessimilarly:

get_property(ResultVariableTARGETTargetNamePROPERTYCXX_STANDARD)

Seecmake-propertiesforalistingofallknownproperties.Youcanalsomakeyourowninsomecases.

.ifstatementsareabitoddinthattheycantakethevariablewithorwithoutthesurroundingsyntax;thisisthereforhistoricalreasons:ifpredatesthe${}syntax.↩

.Interfacetargets,forexample,mayhavelimitsoncustompropertiesthatareallowed.↩

2

1

2

VariablesandtheCache

23

ProgramminginCMake

Controlflow

CMakehasanifstatement,thoughovertheyearsithasbecomerathercomplex.Thereareaseriesofallcapskeywordsyoucanuseinsideanifstatement,andyoucanoftenrefertovariablesbyeitherdirectlybynameorusingthe${}syntax(theifstatementhistoricallypredatesvariableexpansion).Anexampleifstatement:

if(variable)

#Ifvariableis`ON`,`YES`,`TRUE`,`Y`,ornonzeronumber

else()

#Ifvariableis`0`,`OFF`,`NO`,`FALSE`,`N`,`IGNORE`,`NOTFOUND`,`""`,orendsin`-NOTFOUND`

endif()

#Ifvariabledoesnotexpandtooneoftheabove,CMakewillexpanditthentryagain

Sincethiscanbealittleconfusingifyouexplicitlyputavariableexpansion,like${variable},duetothepotentialexpansionofanexpansion,apolicy(CMP0054)wasaddedinCMake3.1+thatkeepsaquotedexpansionfrombeingexpandedyetagain.So,aslongastheminimumversionofCMakeis3.1+,youcando:

if("${variable}")

#Trueifvariableisnotfalse-like

else()

#Notethatundefinedvariableswouldbe`""`thusfalse

endif()

Thereareavarietyofkeywordsaswell,suchas:

Unary:NOT,TARGET,EXISTS(file),DEFINED,etc.Binary:STREQUAL,AND,OR,MATCHES(regularexpression),VERSION_LESS,VERSION_LESS_EQUAL(CMake3.7+),etc.Parenthesescanbeusedtogroup

generator-expressions

generator-expressionsarereallypowerful,butabitoddandspecialized.MostCMakecommandshappenatconfiguretime,includetheifstatementsseenabove.Butwhatifyouneedlogictooccuratbuildtimeoreveninstalltime?Generatorexpressionswereaddedforthispurpose. Theyareevaluatedintargetproperties.

Thesimplestgeneratorexpressionsareinformationalexpressions,andareoftheform$<KEYWORD>;theyevaluatetoapieceofinformationrelevantforthecurrentconfiguration.Theotherformis$<KEYWORD:value>,whereKEYWORDisakeywordthatcontrolstheevaluation,andvalueistheitemtoevaluate(aninformationalexpressionkeywordisallowedhere,too).IfKEYWORDisageneratorexpressionorvariablethatevaluatesto0or1,valueissubstitutedif1andnotif0.Youcannestgeneratorexpressions,andyoucanusevariablestomakereadingnestedvariablesbearable.Someexpressionsallowmultiplevalues,separatedbycommas.

IfyouwanttoputacompileflagonlyfortheDEBUGconfiguration,forexample,youcoulddo:

target_compile_options(MyTargetPRIVATE"$<$<CONFIG:Debug>:--my-flag>")

Thisisanewer,betterwaytoaddthingsthanusingspecialized*_DEBUGvariables,andgeneralizedtoallthethingsgeneratorexpressionssupport.Notethatyoushouldnever,neverusetheconfiguretimevalueforthecurrentconfiguration,becausemulti-configurationgeneratorslikeIDEsdonothavea"current"configurationatconfiguretime,onlyatbuildtimethroughgeneratorexpressionsandcustom*_<CONFIG>variables.

Othercommonusesforgeneratorexpressions:

1

2

ProgramminginCMake

24

Limitinganitemtoacertainlanguageonly,suchasCXX,toavoiditmixingwithsomethinglikeCUDA,orwrappingitsothatitisdifferentdependingontargetlanguage.Accessingconfigurationdependentproperties,liketargetfilelocation.Givingadifferentlocationforbuildandinstalldirectories.

Thatlastoneisverycommon.You'llseesomethinglikethisinalmosteverypackagethatsupportsinstalling:

target_include_directories(

MyTarget

PUBLIC

$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

$<INSTALL_INTERFACE:include>

)

MacrosandFunctions

YoucandefineyourownCMakefunctionormacroeasily.Theonlydifferencebetweenafunctionandamacroisscope;macrosdon'thaveone.So,ifyousetavariableinafunctionandwantittobevisibleoutside,you'llneedPARENT_SCOPE.Nestingfunctionsthereforeisabittricky,sinceyou'llhavetoexplicitlysetthevariablesyouwantvisibletotheoutsideworldtoPARENT_SCOPEineachfunction.But,functionsdon't"leak"alltheirvariableslikemacrosdo.Forthefollowingexamples,I'llusefunctions.

Anexampleofasimplefunctionisasfollows:

function(SIMPLEREQUIRED_ARG)

message(STATUS"Simplearguments:${REQUIRED_ARG},followedby${ARGV}")

set(${REQUIRED_ARG}"FromSIMPLE"PARENT_SCOPE)

endfunction()

simple(This)

message("Output:${This}")

Ifyouwantpositionalarguments,theyarelistedexplicitly,andallotherargumentsarecollectedinARGN(ARGVholdsallarguments,eventheonesyoulist).YouhavetoworkaroundthefactthatCMakedoesnothavereturnvaluesbysettingvariables.Intheexampleabove,youcanexplicitlygiveavariablenametoset.

ArgumentsCMakehasanamedvariablesystemthatyou'vealreadyseeninmostofthebuildinCMakefunctions.Youcanuseitwiththecmake_parse_argumentsfunction.IfyouwanttosupportaversionofCMakelessthan3.5,you'llwanttoalsoincludetheCMakeParseArgumentsmodule,whichiswhereitusedtolivebeforebecomingabuiltincommand.Hereisanexampleofhowtouseit:

function(COMPLEX)

cmake_parse_arguments(

COMPLEX_PREFIX

"SINGLE;ANOTHER"

"ONE_VALUE;ALSO_ONE_VALUE"

"MULTI_VALUES"

${ARGN}

)

endfunction()

complex(SINGLEONE_VALUEvalueMULTI_VALUESsomeothervalues)

Insidethefunctionafterthiscall,you'llfind:

COMPLEX_PREFIX_SINGLE=TRUE

COMPLEX_PREFIX_ANOTHER=FALSE

COMPLEX_PREFIX_ONE_VALUE="value"

COMPLEX_PREFIX_ALSO_ONE_VALUE=<UNDEFINED>

ProgramminginCMake

25

COMPLEX_PREFIX_MULTI_VALUES="some;other;values"

Ifyoulookattheofficialpage,you'llseeaslightlydifferentmethodusingsettoavoidexplicitlywritingthesemicolonsinthelist;feelfreetousethestructureyoulikebest.Youcanmixitwiththepositionalargumentslistedabove;anyremainingarguments(thereforeoptionalpositionalarguments)areinCOMPLEX_PREFIX_UNPARSED_ARGUMENTS.

.Theyactasiftheyareevaluatedatbuild/installtime,thoughactuallytheyareevaluatedforeachbuildconfiguration.↩

.TheCMakedocssplitsexpressionsintoInformational,Logical,andOutput.↩

1

2

ProgramminginCMake

26

Communicationwithyourcode

ConfigureFile

CMakeallowsyoutoaccessCMakevariablesfromyourcodeusingconfigure_file.Thiscommandcopiesafile(traditionallyendingin.infromoneplacetoanother,substitutingallCMakevariablesitfinds.Ifyouwanttoavoidreplacingexisting${}syntaxinyourinputfile,usethe@ONLYkeyword.There'salsoaCOPY_ONLYkeywordifyouarejustusingthisasareplacementforfile(COPY.

Thisfunctionalityisusedquitefrequently;forexample,onVersion.h.in:

Version.h.in

#pragmaonce

#defineMY_VERSION_MAJOR@PROJECT_VERSION_MAJOR@

#defineMY_VERSION_MINOR@PROJECT_VERSION_MINOR@

#defineMY_VERSION_PATCH@PROJECT_VERSION_PATCH@

#defineMY_VERSION_TWEAK@PROJECT_VERSION_TWEAK@

#defineMY_VERSION"@PROJECT_VERSION@"

CMakelines:

configure_file(

"${PROJECT_SOURCE_DIR}/include/My/Version.h.in"

"${PROJECT_BINARY_DIR}/include/My/Version.h"

)

Youshouldincludethebinaryincludedirectoryaswellwhenbuildingyourproject.Ifyouwanttoputanytrue/falsevariablesinaheader,CMakehasCspecific#cmakedefineand#cmakedefine01replacementstomakeappropriatedefinelines.

Youcanalso(andoftendo)usethistoproduce.cmakefiles,suchastheconfigurefiles(seeinstalling).

Readingfiles

Theotherdirectioncanbedonetoo;youcanreadinsomething(likeaversion)fromyoursourcefiles.Ifyouhaveaheaderonlylibrarythatyou'dliketomakeavailablewithorwithoutCMake,forexample,thenthiswouldbethebestwaytohandleaversion.Thiswouldlooksomethinglikethis:

#Assumingthecanonicalversionislistedinasingleline

#ThiswouldbeinseveralpartsifpickingupfromMAJOR,MINOR,etc.

set(VERSION_REGEX"#defineMY_VERSION[\t]+\"(.+)\"")

#Readinthelinecontainingtheversion

file(STRINGS"${CMAKE_CURRENT_SOURCE_DIR}/include/My/Version.hpp"

VERSION_STRINGREGEX${VERSION_REGEX})

#Pickoutjusttheversion

string(REGEXREPLACE${VERSION_REGEX}"\\1"VERSION_STRING"${VERSION_STRING}")

#AutomaticallygettingPROJECT_VERSION_MAJOR,My_VERSION_MAJOR,etc.

project(MyLANGUAGESCXXVERSION${VERSION_STRING})

Above,file(STRINGSfile_namevariable_nameREGEXregex)pickslinesthatmatcharegex;andthesameregexisusedtothenpickouttheparenthesescapturegroupwiththeversionpart.Replaceisusedwithbacksubstitutiontooutputonlythatonegroup.

Communicatingwithyourcode

27

Communicatingwithyourcode

28

HowtostructureyourprojectThefollowinginformationisbiased.Butinagoodway,Ithink.I'mgoingtotellyouhowtostructurethedirectoriesinyourproject.Thisisbasedonconvention,butwillhelpyou:

Easilyreadotherprojectsfollowingthesamepatters,Avoidapatternthatcausesconflicts,Keepfrommuddlingandcomplicatingyourbuild.

First,thisiswhatyourfilesshouldlooklikewhenyoustartifyourprojectiscreativelycalledproject,withalibrarycalledlib,andaexecutablecalledapp:

-project

-.gitignore

-README.md

-LICENCE.md

-CMakeLists.txt

-cmake

-FindSomeLib.cmake

-something_else.cmake

-include

-project

-lib.hpp

-src

-CMakeLists.txt

-lib.cpp

-apps

-CMakeLists.txt

-app.cpp

-tests

-CMakeLists.txt

-testlib.cpp

-docs

-CMakeLists.txt

-extern

-googletest

-scripts

-helper.py

Thenamesarenotabsolute;you'llseecontentionabouttest/vs.tests/,andtheapplicationfoldermaybecalledsomethingelse(ornotexistforalibrary-onlyproject).You'llalsosometimeseeapythonfolderforpythonbindings,oracmakefolderforhelperCMakefiles,likeFind<library>.cmakefiles.Butthebasicsarethere.

Noticeafewthingsalreadyapparent;theCMakeLists.txtfilesaresplitupoverallsourcedirectories,andarenotintheincludedirectories.Thisisbecauseyoushouldbeabletocopythecontentsoftheincludedirectoryto/usr/includeorsimilardirectly(exceptforconfigurationheaders,whichIgooverinanotherchapter),andnothaveanyextrafilesorcauseanyconflicts.That'salsowhythereisadirectoryforyourprojectinsidetheincludedirectory.Useadd_subdirectorytoaddasubdirectorycontainingaCMakeLists.txt.

Youoftenwantacmakefolder,withallofyourhelpermodules.ThisiswhereyourFind*.cmakefilesgo.Ansetofsomecommonhelpersisatgithub.com/CLIUtils/cmake.ToaddthisfoldertoyourCMakepath:

set(CMAKE_MODULE_PATH"${PROJECT_SOURCE_DIR}/cmake"${CMAKE_MODULE_PATH})

Yourexternfoldershouldcontaingitsubmodulesalmostexclusively.Thatway,youcancontroltheversionofthedependenciesexplicitly,butstillupgradeeasily.SeetheTestingchapterforanexampleofaddingasubmodule.

Youshouldhavesomethinglike/build*inyour.gitignore,sothatuserscanmakebuilddirectoriesinthesourcedirectoryandusethosetobuild.Afewpackagesprohibitthis,butit'smuchbetterthandoingatrueout-of-sourcebuildandhavingtotypesomethingdifferentforeachpackageyoubuild.

HowtoStructureYourProject

29

Ifyouwanttoavoidthebuilddirectorybeinginavalidsourcedirectory,addthisnearthetopofyourCMakeLists:

###Requireout-of-sourcebuilds

file(TO_CMAKE_PATH"${PROJECT_BINARY_DIR}/CMakeLists.txt"LOC_PATH)

if(EXISTS"${LOC_PATH}")

message(FATAL_ERROR"Youcannotbuildinasourcedirectory(oranydirectorywithaCMakeLists.txtfile).Pleasemakeab

uildsubdirectory.FeelfreetoremoveCMakeCache.txtandCMakeFiles.")

endif()

Seetheextendedcodeexamplehere.

HowtoStructureYourProject

30

Runningotherprograms

Runningacommandatconfiguretime

Runningacommandatconfiguretimeisrelativelyeasy.Useexecute_processtorunaprocessandaccesstheresults.ItisgenerallyagoodideatoavoidhardcodingaprogrampathintoyourCMake;youcanuse${CMAKE_COMMAND},find_package(Git),orfind_programtogetaccesstoacommandtorun.UseRESULT_VARIABLEtocheckthereturncodeandOUTPUT_VARIABLEtogettheoutput.

Hereisanexamplethatupdatesallgitsubmodules:

find_package(GitQUIET)

if(GIT_FOUNDANDEXISTS"${PROJECT_SOURCE_DIR}/.git")

execute_process(COMMAND${GIT_EXECUTABLE}submoduleupdate--init--recursive

WORKING_DIRECTORY${CMAKE_CURRENT_SOURCE_DIR}

RESULT_VARIABLEGIT_SUBMOD_RESULT)

if(NOTGIT_SUBMOD_RESULTEQUAL"0")

message(FATAL_ERROR"gitsubmoduleupdate--initfailedwith${GIT_SUBMOD_RESULT},pleasecheckoutsubmodules")

endif()

endif()

RunningacommandatbuildtimeBuildtimecommandsareabittricker.Themaincomplicationcomesfromthetargetsystem;whendoyouwantyourcommandtorun?Doesitproduceanoutputthatanothertargetneeds?Withthisinmind,hereisanexamplethatcallsaPythonscripttogenerateaheaderfile:

find_package(PythonInterpREQUIRED)

add_custom_command(OUTPUT"${CMAKE_CURRENT_BINARY_DIR}/include/Generated.hpp"

COMMAND"${PYTHON_EXECUTABLE}""${CMAKE_CURRENT_SOURCE_DIR}/scripts/GenerateHeader.py"--argument

DEPENDSsome_target)

add_custom_target(generate_headerALL

DEPENDS"${CMAKE_CURRENT_BINARY_DIR}/include/Generated.hpp")

install(FILES${CMAKE_CURRENT_BINARY_DIR}/include/Generated.hppDESTINATIONinclude)

Here,thegenerationhappensaftersome_targetiscomplete,andhappenswhenyourunmakewithoutatarget(ALL).Ifyoumakethisadependencyofanothertargetwithadd_dependencies,youcouldavoidtheALLkeyword.Or,youcouldrequirethatauserexplicitlybuildsthegenerate_headertargetwhenmaking.

Includedcommonutilities

AusefultoolinwritingCMakebuildsthatworkcross-platformiscmake-E<mode>(seeninCMakefilesas${CMAKE_COMMAND}-E).ThismodeallowsCMaketodoavarietyofthingswithoutcallingsystemtoolsexplicitly,likecopy,make_directory,andremove.Itismostlyusedforthebuildtimecommands.Notethattheveryusefulcreate_symlinkmodeusedtobeUnixonly,butwasaddedforWindowsinCMake3.13.Seethedocs.

RunningOtherPrograms

31

AsimpleexampleThisisasimpleyetcompleteexampleofaproperCMakeLists.Forthisprogram,wehaveonelibrary(MyLibExample)withaheaderfileandasourcefile,andoneapplication,MyExample,withonesourcefile.

#AlmostallCMakefilesshouldstartwiththis

#Youshouldalwaysspecifyarangewiththenewest

#andoldesttestedversionsofCMake.Thiswillensure

#youpickupthebestpolicies.

cmake_minimum_required(VERSION3.1...3.16)

#Thisisyourprojectstatement.Youshouldalwayslistlanguages;

#Listingtheversionisniceheresinceitsetslotsofusefulvariables

project(ModernCMakeExampleVERSION1.0LANGUAGESCXX)

#IfyousetanyCMAKE_variables,thatcangohere.

#(Butusuallydon'tdothis,exceptmaybeforC++standard)

#Findpackagesgohere.

#Youshouldusuallysplitthisintofolders,butthisisasimpleexample

#Thisisa"default"library,andwillmatchthe***variablesetting.

#OthercommonchoicesareSTATIC,SHARED,andMODULE

#IncludingheaderfilesherehelpsIDEsbutisnotrequired.

#Outputlibnamematchestargetname,withtheusualextensionsonyoursystem

add_library(MyLibExamplesimple_lib.cppsimple_lib.hpp)

#Linkeachtargetwithothertargetsoraddoptions,etc.

#Addingsomethingwecanrun-Outputnamematchestargetname

add_executable(MyExamplesimple_example.cpp)

#Makesureyoulinkyourtargetswiththiscommand.Itcanalsolinklibrariesand

#evenflags,solinkingatargetthatdoesnotexistwillnotgiveaconfigure-timeerror.

target_link_libraries(MyExamplePRIVATEMyLibExample)

Thecompleteexampleisavailableinexamplesfolder.

Alarger,multi-fileexampleisalsoavailable.

ASimpleExample

32

AddingfeaturesThissectioncoversaddingcommonfeaturestoyourCMakeproject.You'lllearnhowtoaddavarietyofoptionscommonlyneededinC++projects,likeC++11support,aswellashowtosupportIDEsandmore.

Defaultbuildtype

CMakenormallydoesa"non-release,nondebug"emptybuildtype;ifyouprefertosetthedefaultbuildtypeyourself,youcanfollowthisrecipeforthedefaultbuildtypemodifiedfromtheKitwareblog:

set(default_build_type"Release")

if(NOTCMAKE_BUILD_TYPEANDNOTCMAKE_CONFIGURATION_TYPES)

message(STATUS"Settingbuildtypeto'${default_build_type}'asnonewasspecified.")

set(CMAKE_BUILD_TYPE"${default_build_type}"CACHE

STRING"Choosethetypeofbuild."FORCE)

#Setthepossiblevaluesofbuildtypeforcmake-gui

set_property(CACHECMAKE_BUILD_TYPEPROPERTYSTRINGS

"Debug""Release""MinSizeRel""RelWithDebInfo")

endif()

AddingFeatures

33

C++11andbeyondC++11issupportedbyCMake.Really.JustnotinCMake2.8,because,guesswhat,C++11didn'texistin2009when2.0wasreleased.AslongasyouareusingCMake3.1ornewer,youshouldbefine,therearetwodifferentwaystoenablesupport.Andasyou'llsoonsee,there'sevenbettersupportinCMake3.8+.I'llstartwiththat,sincethisisModernCMake.

CMake3.8+:Metacompilerfeatures

AslongasyoucanrequirethatauserinstallCMake,you'llhaveaccesstothenewestwaytoenableC++standards.Thisisthemostpowerfulway,withthenicestsyntaxandthebestsupportfornewstandards,andthebesttargetbehaviorformixingstandardsandoptions.AssumingyouhaveatargetnamedmyTarget,itlookslikethis:

target_compile_features(myTargetPUBLICcxx_std_11)

set_target_properties(myTargetPROPERTIESCXX_EXTENSIONSOFF)

Forthefirstline,wegettopickbetweencxx_std_11,cxx_std_14,andcxx_std_17.Thesecondlineisoptional,butwillavoidextensionsbeingadded;withoutityou'dgetthingslike-std=g++11replacing-std=c++11.ThefirstlineevenworksonINTERFACEtargets;onlyactualcompiledtargetscanusethesecondline.

IfatargetfurtherdownthedependencychainspecifiesahigherC++level,thisinteractsnicely.It'sreallyjustamoreadvancedversionofthefollowingmethod,soitinteractsnicelywiththat,too.

CMake3.1+:Compilerfeatures

Youcanaskforspecificcompilerfeaturestobeavailable.ThiswasmoregranularthanaskingforaC++version,thoughit'sabittrickytopickoutjustthefeaturesapackageisusingunlessyouwrotethepackageandhaveagoodmemory.SincethisultimatelychecksagainstalistofoptionsCMakeknowsyourcompilersupportsandpicksthehighestflagindicatedinthatlist,youdon'thavetospecifyalltheoptionsyouneed,justtherarestones.Thesyntaxisidenticaltothesectionabove,youjusthavealistofoptionstopickinsteadofcxx_std_*options.Seethewholelisthere.

Ifyouhaveoptionalfeatures,youcanusethelistCMAKE_CXX_COMPILE_FEATURESanduseif(...IN_LIST...)fromCMake3.3+toseeifthatfeatureissupported,andadditconditionally.Seethedocshereforotherusecases.

Arelatedfeature,WriteCompilerDetectionHeader,isworthcheckingout.Itisamodulethatletsyoumakeafilewithmacrosallowingyoutocheckandsupportoptionalfeaturesforspecificcompilers.Likeanyheadergenerator,thiswillrequirethatyoubuildwithCMakesothatyourheadercanbegeneratedaspartofthebuildprocess(onlyimportantifyoucareaboutsupportingmultiplebuildsystems,orifyouaremakingano-buildheader-onlylibrary).

CMake3.1+:Globalandpropertysettings

ThereisanotherwaythatC++standardsaresupported;aspecificsetofthreeproperties(bothglobalandtargetlevel).Theglobalpropertiesare:

set(CMAKE_CXX_STANDARD11)

set(CMAKE_CXX_STANDARD_REQUIREDON)

set(CMAKE_CXX_EXTENSIONSOFF)

ThefirstlinesetsaC++standardlevel,andthesecondtellsCMaketouseit,andthefinallineisoptionalandensures-std=c++11vs.somethinglike-std=g++11.Thismethodisn'tbadforafinalpackage,butshouldn'tbeusedbyalibrary.Youcanalsosetthesevaluesonatarget:

C++11andBeyond

34

set_target_properties(myTargetPROPERTIES

CXX_STANDARD11

CXX_STANDARD_REQUIREDYES

CXX_EXTENSIONSNO

)

Whichisbetter,butstilldoesn'thavethesortofexplicitcontrolthatcompilerfeatureshaveforpopulatingPRIVATEandINTERFACEproperties.

YoucanfindmoreinformationaboutthefinaltwomethodsonCraigScott'susefulblogpost.

Don'tsetmanualflagsyourself.You'llthenbecomeresponsibleformaintingcorrectflagsforeveryreleaseofeverycompiler,errormessagesforunsupportedcompilerswon'tbeuseful,andsomeIDEsmightnotpickupthemanualflags.

C++11andBeyond

35

AddingFeaturesTherearelotsofcompilerandlinkersettings.Whenyouneedtoaddsomethingspecial,youcouldcheckfirsttoseeifCMakesupportsit;ifitdoes,youcanavoidexplicitlytyingyourselftoacompilerversion.And,betteryet,youexplainwhatyoumeaninyourCMakeLists,ratherthanspewingflags.

ThefirstandmostcommonfeaturewasC++standardssupport,whichgotit'sownchapter.

Positionindependentcode

Thisisbestknownasthe-fPICflag.Muchofthetime,youdon'tneedtodoanything.CMakewillincludetheflagforSHAREDorMODULElibraries.Ifyoudoexplicitlyneedit:

set(CMAKE_POSITION_INDEPENDENT_CODEON)

willdoitglobally,or:

set_target_properties(lib1PROPERTIESPOSITION_INDEPENDENT_CODEON)

toexplicitlyturnitON(orOFF)foratarget.

LittlelibrariesIfyouneedtolinktothedllibrary,with-ldlonLinux,justusethebuilt-inCMakevariable${CMAKE_DL_LIBS}inatarget_link_librariescommand.Nomoduleorfind_packageneeded.(Thisaddswhateverisneededtogetdlopenanddlclose)

Unfortunately,themathlibraryisnotsolucky.Ifyouneedtoexplicitlylinktoit,youcanalwaysdotarget_link_libraries(MyTargetPUBLICm),butitmightbebettertouseCMake'sgenericfind_library:

find_library(MATH_LIBRARYm)

if(MATH_LIBRARY)

target_link_libraries(MyTargetPUBLIC${MATH_LIBRARY})

endif()

YoucanprettyeasilyfindFind*.cmake'sforthisandotherlibrariesthatyouneedwithaquicksearch;mostmajorpackageshaveahelperlibraryofCMakemodules.Seethechapteronexistingpackageinclusionformore.

InterproceduraloptimizationINTERPROCEDURAL_OPTIMIZATION,bestknownaslinktimeoptimizationandthe-fltoflag,isavailableonveryrecentversionsofCMake.YoucanturnthisonwithCMAKE_INTERPROCEDURAL_OPTIMIZATION(CMake3.9+only)ortheINTERPROCEDURAL_OPTIMIZATIONpropertyontargets.SupportforGCCandClangwasaddedinCMake3.8.Ifyousetcmake_minimum_required(VERSION3.9)orbetter(seeCMP0069),settingthistoONonatargetisanerrorifthecompilerdoesn'tsupportit.Youcanusecheck_ipo_supported(),fromthebuilt-inCheckIPOSupportedmodule,toseeifsupportisavailablebeforehand.Anexampleof3.9styleusage:

include(CheckIPOSupported)

check_ipo_supported(RESULTresult)

if(result)

set_target_properties(fooPROPERTIESINTERPROCEDURAL_OPTIMIZATIONTRUE)

endif()

Smallbutcommonneeds

36

Smallbutcommonneeds

37

CCacheandUtilitiesOvertheversions,commonutilitiesthathelpyouwritegoodcodehavehadsupportaddedtoCMake.ThisisusuallyintheformofapropertyandmatchingCMAKE_*initializationvariable.Thefeatureisnotmeanttobetiedtoonespecialprogram,butratheranyprogramthatissomewhatsimilarinbehavior.

Allofthesetake;separatedvalues(astandardlistinCMake)thatdescribetheprogramandoptionsthatyoushouldrunonthesourcefilesofthistarget.

CCache

SettheCMAKE_<LANG>_COMPILER_LAUNCHERvariableorthe<LANG>_COMPILER_LAUNCHERpropertyonatargettousesomethinglikeCCacheto"wrap"thecompilationofthetarget.SupportforCCachehasbeenexpandinginthelatestversionsofCMake.Inpractice,thistendstolooklikethis:

find_program(CCACHE_PROGRAMccache)

if(CCACHE_PROGRAM)

set(CMAKE_CXX_COMPILER_LAUNCHER"${CCACHE_PROGRAM}")

set(CMAKE_CUDA_COMPILER_LAUNCHER"${CCACHE_PROGRAM}")#CMake3.9+

endif()

Utilities

SetthefollowingpropertiesorCMAKE_*initializervariablestothecommandlineforthetools.MostofthemarelimitedtoCorCXXwithmakeorninjagenerators.

<LANG>_CLANG_TIDY:CMake3.6+<LANG>_CPPCHECK

<LANG>_CPPLINT

<LANG>_INCLUDE_WHAT_YOU_USE

ClangtidyHereisasimpleexampleofusingClang-Tidy:

if(CMAKE_VERSIONVERSION_GREATER3.6)

#Addclang-tidyifavailable

option(CLANG_TIDY_FIX"PerformfixesforClang-Tidy"OFF)

find_program(

CLANG_TIDY_EXE

NAMES"clang-tidy"

DOC"Pathtoclang-tidyexecutable"

)

if(CLANG_TIDY_EXE)

if(CLANG_TIDY_FIX)

set(CMAKE_CXX_CLANG_TIDY"${CLANG_TIDY_EXE}""-fix")

else()

set(CMAKE_CXX_CLANG_TIDY"${CLANG_TIDY_EXE}")

endif()

endif()

endif()

Utilities

38

The-fixpartisoptional,andwillmodifyyoursourcefilestotrytofixthetidywarningissued.Ifyouareworkinginagitrepository,thisisfairlysafeasyoucanseewhathaschanged.However,makesureyoudonotrunyourmakefile/ninjabuildinparallel!Thiswillnotworkverywellatallifittriestofixthesameheadertwice.

Ifyouwanttoexplicitlyusethetargetformtoensureyouonlycallthisonyourlocaltargets,youcansetavariable(IusuallychoseDO_CLANG_TIDY)insteadoftheCMAKE_CXX_CLANG_TIDYvariable,thenaddittoyourtargetpropertiesasyoucreatethem.

Includewhatyouuse

Thisisanexampleforusingincludewhatyouuse.First,you'llneedtohavethetool,suchasinadockercontainer:

Then,youcanpassthisintoyourbuildwithoutmodifyingthesource:

Finally,youcancollecttheoutputandapplythefixes:

LinkwhatyouuseThereisabooleantargetproperty,LINK_WHAT_YOU_USE,thatwillcheckforextraneousfileswhenlinking.

Clang-formatClang-formatdoesn'treallyhaveanintegrationwithCMake,unfortunately.Youcouldmakeacustomtarget(Seethispost,oryoucanrunitmanually.AninterestingprojectthatIhavenotreallytriedishere;itaddsaformattargetandevenmakessurethatyoucan'tcommitunformattedfiles.

Thefollowingtwolinewoulddothatinagitrepositoryinbash(assumingyouhavea.clang-formatfile):

gitbook$dockerrun--rm-ittuxity/include-what-you-use:clang_4.0

build#cmake..-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=include-what-you-use

build#make2>iwyu.out

build#fix_includes.py<iwyu.out

gitbook$gitls-files--'*.cpp''*.h'|xargsclang-format-i-style=file

gitbook$gitdiff--exit-code--color

Utilities

39

UsefulModulesThereareatonofusefulmodulesinCMake'smodulescollection;butsomeofthemaremoreusefulthanothers.Hereareafewhighlights.

CMakeDependentOption

Thisaddsacommandcmake_dependent_optionthatsetsanoptionbasedonanothersetofvariablesbeingtrue.Itlookslikethis:

include(CMakeDependentOption)

cmake_dependent_option(BUILD_TESTS"Buildyourtests"ON"VAL1;VAL2"OFF)

whichisjustashortcutforthis:

if(VAL1ANDVAL2)

set(BUILD_TESTS_DEFAULTON)

else()

set(BUILD_TESTS_DEFAULTOFF)

endif()

option(BUILD_TESTS"Buildyourtests"${BUILD_TESTS_DEFAULT})

if(NOTBUILD_TESTS_DEFAULT)

mark_as_advanced(BUILD_TESTS)

endif()

NotethatBUILD_TESTINGisabetterwaytocheckfortestingbeingenabledifyouuseinclude(CTest),sinceitisdefinedforyou.ThisisjustanexampleofCMakeDependentOption.

CMakePrintHelpers

Thismodulehasacoupleofhandyoutputfunctions.cmake_print_propertiesletsyoueasilyprintproperties.Andcmake_print_variableswillprintthenamesandvaluesofanyvariablesyougiveit.

CheckCXXCompilerFlag

Thischeckstoseeifaflagissupported.Forexample:

include(CheckCXXCompilerFlag)

check_cxx_compiler_flag(-someflagOUTPUT_VARIABLE)

NotethatOUTPUT_VARIABLEwillalsoappearintheconfigurationprintout,sochooseagoodname.

Thisisjustoneofmanysimilarmodules,suchasCheckIncludeFileCXX,CheckStructHasMember,TestBigEndian,andCheckTypeSizethatallowyoutocheckforinformationaboutthesystem(andyoucancommunicatethattoyoursourcecode).

WriteCompilerDetectionHeader

Thisisanamazingmodulesimilartotheoneslistedabove,butspecialenoughtodeserveitsownsection.Itallowsyoutolookforalistoffeaturesthatsomecompilerssupport,andwriteoutaC++headerfilethatletsyouknowwhetherthatfeatureisavailable.Itevencanprovidecompatibilitymacrosforfeaturesthathavechangednames!

Usefulmodules

40

Touse:

write_compiler_detection_header(

FILEmyoutput.h

PREFIXMy

COMPILERSGNUClangMSVCIntel

FEATUREScxx_variadic_templates

)

Thissupportscompilerfeatures(definedto0or1),symbols(definedtoemptyorthesymbol),andmacrosthatsupportdifferentnames.TheywillbeprefixedwiththePREFIXyouprovide.YoucanseparatecompilersintodifferentfilesusingOUTPUT_FILES_DIR.

Thedownsideisthatyoudohavetolistthecompilersyouexpecttosupport.IfyouusetheALLOW_UNKNOWN_COMPILERSflag(s),youcankeepthisfromerroringonunknowncompilers,butitwillstillleaveallfeaturesempty.

try_compile/try_run

Thisisnotexactlyamodule,butiscrucialtomanyofthemoduleslistedabove.Youcanattempttocompile(andpossiblyrun)abitofcodeatconfiguretime.Thiscanallowyoutogetinformationaboutthecapabilitiesofyoursystem.Thebasicsyntaxis:

try_compile(

RESULT_VAR

bindir

SOURCES

source.cpp

)

Therearelotsofoptionsyoucanadd,likeCOMPILE_DEFINITIONS.InCMake3.8+,thiswillhonortheCMakeC/C++/CUDAstandardsettings.Ifyouusetry_runinstead,itwillruntheresultingprogramandgiveyoutheoutputinRUN_OUTPUT_VARIABLE.

FeatureSummaryThisisafairlyusefulbutratheroddmodule.Itallowsyoutoprintoutalistofpackageswhatweresearchedfor,aswellasanyoptionsyouexplicitymark.It'spartiallybutnotcompletelytiedintofind_package.Youfirstincludethemodule,asalways:

include(FeatureSummary)

Then,foranyfindpackagesyouhaverunorwillrun,youcanextendthedefaultinformation:

set_package_properties(OpenMPPROPERTIES

URL"http://www.openmp.org"

DESCRIPTION"Parallelcompilerdirectives"

PURPOSE"Thisiswhatitdoesinmypackage")

YoucanalsosettheTYPEofapackagetoRUNTIME,OPTIONAL,RECOMMENDED,orREQUIRED;youcan't,however,lowerthetypeofapackage;ifyouhavealreadyaddedaREQUIREDpackagethroughfind_packagebasedonanoption,you'llseeitlistedasREQUIRED.

And,youcanmarkanyoptionsaspartofthefeaturesummary.Ifyouchoosethesamenameasapackage,thetwointeractwitheachother.

add_feature_info(WITH_OPENMPOpenMP_CXX_FOUND"OpenMP(ThreadsafeFCNsonly)")

Then,youcanprintoutthesummaryoffeatures,eithertothescreenoralogfile:

if(CMAKE_PROJECT_NAMESTREQUALPROJECT_NAME)

feature_summary(WHATENABLED_FEATURESDISABLED_FEATURESPACKAGES_FOUND)

Usefulmodules

41

feature_summary(FILENAME${CMAKE_CURRENT_BINARY_DIR}/features.logWHATALL)

endif()

YoucanbuildanycollectionofWHATitemsthatyoulike,orjustuseALL.

Usefulmodules

42

SupportingIDEsIngeneral,IDEsarealreadysupportedbyastandardCMakeproject.TherearejustafewextrathingsthatcanhelpIDEsperformevenbetter.

Foldersfortargets

SomeIDEs,likeXcode,supportfolders.YouhavetomanuallyenabletheUSE_FOLDERSglobalpropertytoallowCMaketoorganizeyourfilesbyfolders:

set_property(GLOBALPROPERTYUSE_FOLDERSON)

Then,youcanaddtargetstofoldersafteryoucreatethem:

set_property(TARGETMyFilePROPERTYFOLDER"Scripts")

Folderscanbenestedwith/.

Youcancontrolhowfilesshowupineachfolderwithregularexpressionsorexplicitlistingsinsource_group:

FoldersforfilesYoucanalsocontrolhowthefoldersinsidetargetsappear.Therearetwoways,bothusingthesource_groupcommand.Thetraditionalwayis

source_group("SourceFiles\\NewDirectory"REGULAR_EXPRESSION".*\\.c[ucp]p?")

YoucanexplicitlylistfileswithFILES,oruseaREGULAR_EXPRESSION.Thiswayyouhavecompletecontroloverthefolderstructure.However,ifyouron-disklayoutiswelldesigned,youmightjustwanttomimicthat.InCMake3.8+,youcandosoveryeasilywithanewversionofthesource_groupcommand:

source_group(TREE"${CMAKE_CURRENT_SOURCE_DIR}/base/dir"PREFIX"HeaderFiles"FILES${FILE_LIST})

FortheTREEoption,youshouldusuallygiveafullpathstartingwithsomethinglike${CMAKE_CURRENT_SOURCE_DIR}/(becausethecommandinterpretspathsrelativetothebuilddirectory).TheprefixtellsyouwhereitputsitintotheIDEstructure,andtheFILESoptiontakesalistoffiles.CMakewillstriptheTREEpathfromtheFILE_LISTpath,itwilladdPREFIX,andthatwillbetheIDEfolderstructure.

Note:IfyouneedtosupportCMake<3.8,Iwouldrecommendjustprotectingtheabovecommand,andonlysupportingnicefolderlayoutonCMake3.8+.Foroldermethodstodothisfolderlayout,seethisblogpost.

RunningwithanIDE

TouseanIDE,eitherpass-G"nameofIDE"ifCMakecanproducethatIDE'sfiles(likeXcode,VisualStudio),oropentheCMakeLists.txtfilefromyourIDEifthatIDEhasbuiltinsupportforCMake(CLion,QtCreator,manyothers).

IDEs

43

DebuggingcodeYoumightneedtodebugyourCMakebuild,ordebugyourC++code.Botharecoveredhere.

CMakedebugging

First,let'slookatwaystodebugaCMakeListsorotherCMakefile.

Printingvariables

ThetimehonoredmethodofprintstatementslookslikethisinCMake:

message(STATUS"MY_VARIABLE=${MY_VARIABLE}")

However,abuiltinmodulemakesthiseveneasier:

include(CMakePrintHelpers)

cmake_print_variables(MY_VARIABLE)

Ifyouwanttoprintoutaproperty,thisismuch,muchnicer!Insteadofgettingthepropertiesonebyoneofofeachtarget(orotheritemwithproperties,suchasSOURCES,DIRECTORIES,TESTS,orCACHE_ENTRIES-globalpropertiesseemtobemissingforsomereason),youcansimplylistthemandgetthemprinteddirectly:

cmake_print_properties(

TARGETSmy_target

PROPERTIESPOSITION_INDEPENDENT_CODE

)

Tracingarun

HaveyouwantedtowatchexactlywhathappensinyourCMakefile,andwhen?The--trace-source="filename"featureisfantastic.Everylineruninthefilethatyougivewillbeechoedtothescreenwhenitisrun,lettingyoufollowexactlywhatishappening.Therearerelatedoptionsaswell,buttheytendtoburyyouinoutput.

Forexample:

cmake-S.-Bbuild--trace-source=CMakeLists.txt

Ifyouadd--trace-expand,thevariableswillbeexpandedintotheirvalues.

Buildingindebugmode

Forsingle-configurationgenerators,youcanbuildyourcodewith-DCMAKE_BUILD_TYPE=Debugtogetdebuggingflags.Inmulti-configurationgenerators,likemanyIDEs,youcanpicktheconfigurationintheIDE.Therearedistinctflagsforthismode(variablesendingin_DEBUGasopposedto_RELEASE),aswellasageneratorexpressionvalueCONFIG:DebugorCONFIG:Release.

Onceyoumakeadebugbuild,youcanrunadebugger,suchasgdborlldbonit.

Debugging

44

IncludingSmallProjectsThisiswhereagoodGitsystemplusCMakeshines.Youmightnotbeabletosolvealltheworld'sproblems,butthisisprettycloseforC++!

Thereareseveralmethodslistedinthechaptersinthissection.

IncludingProjects

45

GitSubmoduleMethodIfyouwanttoaddaGitrepositoryonthesameservice(GitHub,GitLab,BitBucket,etc),thefollowingisthecorrectGitcommandtosetthatupasasubmoduleintheexterndirectory:

Therelativepathtotherepoisimportant;itallowsyoutokeepthesameaccessmethod(sshorhttps)astheparentrepository.Thisworksverywellinmostways.Whenyouareinsidethesubmodule,youcantreatitjustlikeanormalrepo,andwhenyouareintheparentrepository,youcan"add"tochangethecurrentcommitpointer.

Butthetraditionaldownsideisthatyoueitherhavetohaveyourusersknowgitsubmodulecommands,sotheycaninitandupdatetherepo,ortheyhavetoadd--recursivewhentheyinitiallycloneyourrepo.CMakecanofferasolution:

find_package(GitQUIET)

if(GIT_FOUNDANDEXISTS"${PROJECT_SOURCE_DIR}/.git")

#Updatesubmodulesasneeded

option(GIT_SUBMODULE"Checksubmodulesduringbuild"ON)

if(GIT_SUBMODULE)

message(STATUS"Submoduleupdate")

execute_process(COMMAND${GIT_EXECUTABLE}submoduleupdate--init--recursive

WORKING_DIRECTORY${CMAKE_CURRENT_SOURCE_DIR}

RESULT_VARIABLEGIT_SUBMOD_RESULT)

if(NOTGIT_SUBMOD_RESULTEQUAL"0")

message(FATAL_ERROR"gitsubmoduleupdate--initfailedwith${GIT_SUBMOD_RESULT},pleasecheckoutsubmodules")

endif()

endif()

endif()

if(NOTEXISTS"${PROJECT_SOURCE_DIR}/extern/repo/CMakeLists.txt")

message(FATAL_ERROR"Thesubmoduleswerenotdownloaded!GIT_SUBMODULEwasturnedofforfailed.Pleaseupdatesubmodules

andtryagain.")

endif()

ThefirstlinechecksforGitusingCMake'sbuiltinFindGit.cmake.Then,ifyouareinagitcheckoutofyoursource,addanoption(defaultingtoON)thatallowsdeveloperstoturnoffthefeatureiftheyneedto.Wethenrunthecommandtogetallrepositories,andfailifthatcommandfails,withaniceerrormessage.Finally,weverifythattherepositoriesexistbeforecontinuing,regardlessofthemethodusedtoobtainthem.YoucanuseORtolistseveral.

Now,youruserscanbecompletelyoblivioustotheexistenceofthesubmodules,andyoucanstillkeepupgooddevelopmentpractices!Theonlythingtowatchoutforisfordevelopers;youwillresetthesubmodulewhenyourerunCMakeifyouaredevelopinginsidethesubmodule.Justaddnewcommitstotheparentstagingarea,andyou'llbefine.

YoucanthenincludeprojectsthatprovidegoodCMakesupport:

add_subdirectory(extern/repo)

Or,youcanbuildaninterfacelibrarytargetyourselfifitisaheaderonlyproject.Or,youcanusefind_packageifthatissupported,probablypreparingtheinitialsearchdirectorytobetheoneyou'veadded(checkthedocsorthefilefortheFind*.cmakefileyouareusing).YoucanalsoincludeaCMakehelperfiledirectoryifyouappendtoyourCMAKE_MODULE_PATH,forexampletoaddpybind11'simprovedFindPython*.cmakefiles.

Bonus:Gitversionnumber

MovethistoGitsection:

execute_process(COMMAND${GIT_EXECUTABLE}rev-parse--shortHEAD

WORKING_DIRECTORY"${CMAKE_CURRENT_SOURCE_DIR}"

gitbook$gitsubmoduleadd../../owner/repo.gitextern/repo

Submodule

46

OUTPUT_VARIABLEPACKAGE_GIT_VERSION

ERROR_QUIET

OUTPUT_STRIP_TRAILING_WHITESPACE)

Submodule

47

GoogleTest:Downloadmethod

DownloadingMethod:buildtime

UntilCMake3.11,theprimarydownloadmethodforpackageswasdoneatbuildtime.Thiscausesseveralissues;mostimportantofwhichisthatadd_subdirectorydoesn'tworkonafilethatdoesn'texistyet!Thetoolforthis,ExternalProject,hastoworkaroundthisbydoingthebuilditself.(Itcan,however,buildnon-CMakepackagesaswell).

.NotethatExternalDataisthetoolfornon-packagedata.↩

DownloadingMethod:configuretimeIfyoupreferconfiguretime,seetheCrascit/DownloadProjectrepositoryforadrop-insolution.Submodulesworksowell,though,thatI'vediscontinuedmostofthedownloadsforthingslikeGoogleTestandmovedthemtosubmodules.Autodownloadsarehardertomimicifyoudon'thaveinternetaccess,andtheyareoftenimplementedinthebuilddirectory,wastingtimeandspaceifyouhavemultiplebuilddirectories.

1

1

DownloadProject

48

FetchContent(CMake3.11+)Often,youwouldliketodoyourdownloadofdataorpackagesaspartoftheconfigureinsteadofthebuild.Thiswasinventedseveraltimesinthirdpartymodules,butwasfinallyaddedtoCMakeitselfaspartofCMake3.11astheFetchContentmodule.

TheFetchContentmodulehasexcellentdocumentationthatIwon'ttrytorepeat.Thekeyideasare:

UseFetchContent_Declare(MyName)togetdataorapackage.YoucansetURLs,Gitrepositories,andmore.UseFetchContent_GetProperties(MyName)onthenameyoupickedinthefirststeptogetMyName_*variables.CheckMyName_POPULATED,andifnotpopulated,useFetchContent_Populate(MyName)(andifapackage,add_subdirectory("${MyName_SOURCE_DIR}""${MyName_BINARY_DIR}"))

Forexample,todownloadCatch2:

FetchContent_Declare(

catch

GIT_REPOSITORYhttps://github.com/catchorg/Catch2.git

GIT_TAGv2.9.1

)

#CMake3.14+

FetchContent_MakeAvailable(catch)

Ifyoucan'tuseCMake3.14+,theclassicwaytopreparecodewas:

#CMake3.11+

FetchContent_GetProperties(catch)

if(NOTcatch_POPULATED)

FetchContent_Populate(catch)

add_subdirectory(${catch_SOURCE_DIR}${catch_BINARY_DIR})

endif()

Ofcourse,youcouldbundledthisupintoamacro:

if(${CMAKE_VERSION}VERSION_LESS3.14)

macro(FetchContent_MakeAvailableNAME)

FetchContent_GetProperties(${NAME})

if(NOT${NAME}_POPULATED)

FetchContent_Populate(${NAME})

add_subdirectory(${${NAME}_SOURCE_DIR}${${NAME}_BINARY_DIR})

endif()

endmacro()

endif()

NowyouhavetheCMake3.14+syntaxinCMake3.11+.

Fetch(CMake3.11)

49

Testing

GeneralTestingInformation

InyourmainCMakeLists.txtyouneedtoaddthefollowingfunctioncall(notinasubfolder):

if(CMAKE_PROJECT_NAMESTREQUALPROJECT_NAME)

include(CTest)

endif()

WhichwillenabletestingandsetaBUILD_TESTINGoptionsouserscanturntestingonandoff(Alongwithafewotherthings).Oryoucandothisyourselfbydirectlycallingenable_testing().

Whenyouaddyourtestfolder,youshoulddosomethinglikethis:

if(CMAKE_PROJECT_NAMESTREQUALPROJECT_NAMEANDBUILD_TESTING)

add_subdirectory(tests)

endif()

Thereasonforthisisthatifsomeoneelseincludesyourpackage,andtheyuseBUILD_TESTING,theyprobablydonotwantyourteststobuild.Intherarecasethatyoureallydowanttoenabletestingonbothpackages,youcanprovideanoverride:

if((CMAKE_PROJECT_NAMESTREQUALPROJECT_NAMEORMYPROJECT_BUILD_TESTING)ANDBUILD_TESTING)

add_subdirectory(tests)

endif()

Themainusecasefortheoverrideaboveisactuallyinthisbook'sownexamples,asthemasterCMakeprojectreallydoeswanttorunallthesubprojecttests.

Youcanregistertargetswith:

add_test(NAMETestNameCOMMANDTargetName)

IfyouputsomethingelsebesidesatargetnameafterCOMMAND,itwillregisterasacommandlinetorun.Itwouldalsobevalidtoputthegeneratorexpression:

add_test(NAMETestNameCOMMAND$<TARGET_FILE:${TESTNAME}>)

whichwouldusetheoutputlocation(thus,theexecutable)oftheproducedtarget.

Buildingaspartofatest

IfyouwanttorunCMaketobuildaprojectaspartofatest,youcandothattoo(infact,thisishowCMaketestsitself).Forexample,ifyourmasterprojectwascalledMyProjectandyouhadanexamples/simpleprojectthatcouldbuildbyitself,thiswouldlooklike:

add_test(

NAME

ExampleCMakeBuild

COMMAND

"${CMAKE_CTEST_COMMAND}"

--build-and-test"${My_SOURCE_DIR}/examples/simple"

"${CMAKE_CURRENT_BINARY_DIR}/simple"

--build-generator"${CMAKE_GENERATOR}"

--test-command"${CMAKE_CTEST_COMMAND}"

Testing

50

)

TestingFrameworksLookatthesubchaptersforrecipesforpopularframeworks.

GoogleTest:ApopularoptionfromGoogle.Developmentcanbeabitslow.Catch2:Amodern,PyTest-likeframeworkwithclevermacros.DocTest:AreplacementforCatch2thatissupposedtocompilemuchfasterandbecleaner.SeeCatch2chapterandreplacewithDocTest.

Testing

51

GoogleTest

Submodulemethod(preferred)

Tousethismethod,justcheckoutGoogleTestasasubmodule:

gitsubmoduleadd--branch=release-1.8.0../../google/googletest.gitextern/googletest

Then,inyourmainCMakeLists.txt:

option(PACKAGE_TESTS"Buildthetests"ON)

if(PACKAGE_TESTS)

enable_testing()

include(GoogleTest)

add_subdirectory(tests)

endif()

IwouldrecommendusingsomethinglikePROJECT_NAMESTREQUALCMAKE_PROJECT_NAMEtosetthedefaultforthePACKAGE_TESTSoption,sincethisshouldonlybuildbydefaultifthisisthecurrentproject.Asmentionedbefore,youhavetodotheenable_testinginyourmainCMakeLists.

Now,inyourtestsdirectory:

add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest""extern/googletest")

IfyoudidthisinyourmainCMakeLists,youcoulduseanormaladd_subdirectory;theextrapathhereisneededtocorrectthebuildpathbecausewearecallingitfromasubdirectory.

Thenextlineisoptional,butkeepsyourCACHEcleaner:

mark_as_advanced(

BUILD_GMOCKBUILD_GTESTBUILD_SHARED_LIBS

gmock_build_testsgtest_build_samplesgtest_build_tests

gtest_disable_pthreadsgtest_force_shared_crtgtest_hide_internal_symbols

)

IfyouareinterestedinkeepingIDEsthatsupportfoldersclean,Iwouldalsoaddtheselines:

set_target_properties(gtestPROPERTIESFOLDERextern)

set_target_properties(gtest_mainPROPERTIESFOLDERextern)

set_target_properties(gmockPROPERTIESFOLDERextern)

set_target_properties(gmock_mainPROPERTIESFOLDERextern)

Then,toaddatest,I'drecommendthefollowingmacro:

macro(package_add_testTESTNAME)

#createanexectuableinwhichthetestswillbestored

add_executable(${TESTNAME}${ARGN})

#linktheGoogletestinfrastructure,mockinglibrary,andadefaultmainfuctionto

#thetestexecutable.Removeg_test_mainifwritingyourownmainfunction.

target_link_libraries(${TESTNAME}gtestgmockgtest_main)

#gtest_discover_testsreplacesgtest_add_tests,

#seehttps://cmake.org/cmake/help/v3.10/module/GoogleTest.htmlformoreoptionstopasstoit

gtest_discover_tests(${TESTNAME}

#setaworkingdirectorysoyourprojectrootsothatyoucanfindtestdataviapathsrelativetotheprojectroot

WORKING_DIRECTORY${PROJECT_DIR}

PROPERTIESVS_DEBUGGER_WORKING_DIRECTORY"${PROJECT_DIR}"

1

GoogleTest

52

)

set_target_properties(${TESTNAME}PROPERTIESFOLDERtests)

endmacro()

package_add_test(test1test1.cpp)

Thiswillallowyoutoquicklyandsimplyaddtests.Feelfreetoadjusttosuityourneeds.Ifyouhaven'tseenitbefore,ARGNis"everyargumentafterthelistedones".Modifythemacrotomeetyourneeds.Forexample,ifyou'retestinglibrariesandneedtolinkindifferentlibrariesfordifferenttests,youmightusethis:

macro(package_add_test_with_librariesTESTNAMEFILESLIBRARIESTEST_WORKING_DIRECTORY)

add_executable(${TESTNAME}${FILES})

target_link_libraries(${TESTNAME}gtestgmockgtest_main${LIBRARIES})

gtest_discover_tests(${TESTNAME}

WORKING_DIRECTORY${TEST_WORKING_DIRECTORY}

PROPERTIESVS_DEBUGGER_WORKING_DIRECTORY"${TEST_WORKING_DIRECTORY}"

)

set_target_properties(${TESTNAME}PROPERTIESFOLDERtests)

endmacro()

package_add_test_with_libraries(test1test1.cpplib_to_test"${PROJECT_DIR}/european-test-data/")

Downloadmethod

YoucanusethedownloaderinmyCMakehelperrepository,usingCMake'sincludecommand.

ThisisadownloaderforGoogleTest,basedontheexcellentDownloadProjecttool.DownloadingacopyforeachprojectistherecommendedwaytouseGoogleTest(somuchso,infact,thattheyhavedisabledtheautomaticCMakeinstalltarget),sothisrespectsthatdesigndecision.Thismethoddownloadstheprojectatconfiguretime,sothatIDE'scorrectlyfindthelibraries.Usingitissimple:

cmake_minimum_required(VERSION3.10)

project(MyProjectCXX)

list(APPENDCMAKE_MODULE_PATH${PROJECT_SOURCE_DIR}/cmake)

enable_testing()#Mustbeinmainfile

include(AddGoogleTest)#Couldbein/tests/CMakeLists.txt

add_executable(SimpleTestSimpleTest.cu)

add_gtest(SimpleTest)

Note:add_gtestisjustamacrothataddsgtest,gmock,andgtest_main,andthenrunsadd_testtocreateatestwiththesamename:

target_link_libraries(SimpleTestgtestgmockgtest_main)

add_test(SimpleTestSimpleTest)

FetchContent:CMake3.11TheexamplefortheFetchContentmoduleisGoogleTest:

include(FetchContent)

FetchContent_Declare(

googletest

GIT_REPOSITORYhttps://github.com/google/googletest.git

GIT_TAGrelease-1.8.0

)

FetchContent_GetProperties(googletest)

if(NOTgoogletest_POPULATED)

GoogleTest

53

FetchContent_Populate(googletest)

add_subdirectory(${googletest_SOURCE_DIR}${googletest_BINARY_DIR})

endif()

.HereI'veassumedthatyouareworkingonaGitHubrepositorybyusingtherelativepathtogoogletest.↩1

GoogleTest

54

CatchCatchandCatch2(C++11onlyversion)arepowerful,idomatictestingsolutionssimilarinphilosophytoPyTestforPython.TouseCatchinaCMakeproject,thereareseveraloptions.

Vendoring

IfyousimplydropinthesingleincludereleaseofCatchintoyourproject,thisiswhatyouwouldneedtoaddCatch:

#Prepare"Catch"libraryforotherexecutables

set(CATCH_INCLUDE_DIR${CMAKE_CURRENT_SOURCE_DIR}/extern/catch)

add_library(Catch2::CatchIMPORTEDINTERFACE)

set_property(Catch2::CatchPROPERTYINTERFACE_INCLUDE_DIRECTORIES"${CATCH_INCLUDE_DIR}")

Then,youwouldlinktoCatch2::Catch.ThiswouldhavebeenokayasanINTERFACEtargetsinceyouwon'tbeexportingyourtests.

DirectinclusionIfyouaddthelibraryusingExternalProject,FetchContent,orgitsubmodules,youcanalsoadd_subdirectoryCatch(CMake3.1+).

CatchalsoprovidestwoCMakemodulesthatyoucanusetoregistertheindividualtestswithCMake.

Catch

55

ExportingandInstallingTherearethreegoodwaysandonebadwaytoallowothersuseyourlibrary:

Findmodule(thebadway)

Ifyouarethelibraryauthor,don'tmakeaFind<mypackage>.cmakescript!TheseweredesignedforlibrarieswhoseauthorsdidnotsupportCMake.UseaConfig<mypackage>.cmakeinsteadaslistedbelow.

AddSubproject

Apackagecanincludeyourprojectinasubdirectory,andthenuseadd_directoryonthesubdirectory.Thisusefulforheader-onlyandquick-to-compilelibraries.Notethattheinstallcommandsmayinterferewiththeparentproject,soyoucanaddEXCLUDE_FROM_ALLtotheadd_subdirectorycommand;thetargetsyouexplicitlyusewillstillbebuilt.

Inordertosupportthisasalibraryauthor,makesureyouuseCMAKE_CURRENT_SOURCE_DIRinsteadofPROJECT_SOURCE_DIR(andlikewiseforothervariables,likebinarydirs).YoucancheckCMAKE_PROJECT_NAMESTREQUALPROJECT_NAMEtoonlyaddoptionsordefaultsthatmakesenseifthisisaproject.

Also,sincenamespacesareagoodidea,andtheusageofyourlibraryshouldbeconsistentwiththeothermethodsbelow,youshouldadd

add_library(MyLib::MyLibALIASMyLib)

tostandardisetheusageacrossallmethods.ThisALIAStargetwillnotbeexportedbelow.

ExportingThethirdwayis*Config.cmakescripts;thatwillbethetopicofthenextchapterinthissession.

ExportingandInstalling

56

InstallingInstallcommandscauseafileortargettobe"installed"intotheinstalltreewhenyoumakeinstall.Yourbasictargetinstallcommandlookslikethis:

install(TARGETSMyLib

EXPORTMyLibTargets

LIBRARYDESTINATIONlib

ARCHIVEDESTINATIONlib

RUNTIMEDESTINATIONbin

INCLUDESDESTINATIONinclude

)

Thevariousdestinationsareonlyneededifyouhavealibrary,staticlibrary,orprogramtoinstall.Theincludesdestinationisspecial;sinceatargetdoesnotinstallincludes.Itonlysetstheincludesdestinationontheexportedtarget(whichisoftenalreadysetbytarget_include_directories,sochecktheMyLibTargetsfileandmakesureyoudon'thavetheincludedirectoryincludedtwiceifyouwantcleancmakefiles).

It'susuallyagoodideatogiveCMakeaccesstotheversion,sothatfind_packagecanhaveaversionspecified.Thatlookslikethis:

include(CMakePackageConfigHelpers)

write_basic_package_version_file(

MyLibConfigVersion.cmake

VERSION${PACKAGE_VERSION}

COMPATIBILITYAnyNewerVersion

)

Youhavetwochoicesnext.YouneedtomakeaMyLibConfig.cmake,butyoucandoiteitherbyexportingyourtargetsdirectlytoit,orbywritingitbyhand,thenincludingthetargetsfile.Thelateroptioniswhatyou'llneedifyouhaveanydependencies,evenjustOpenMP,soI'llillustratethatmethod.

First,makeaninstalltargetsfile(verysimilartotheoneyoumadeinthebuilddirectory):

install(EXPORTMyLibTargets

FILEMyLibTargets.cmake

NAMESPACEMyLib::

DESTINATIONlib/cmake/MyLib

)

Thisfilewilltakethetargetsyouexportedandputtheminafile.Ifyouhavenodependencies,justuseMyLibConfig.cmakeinsteadofMyLibTargets.cmakehere.ThenwriteacustomMyLibConfig.cmakefileinyoursourcetreesomewhere.Ifyouwanttocaptureconfiguretimevariables,youcanusea.infile,andyouwillwanttousethe@var@syntax.Thecontentsthatlooklikethis:

include(CMakeFindDependencyMacro)

#Capturingvaluesfromconfigure(optional)

set(my-config-var@my-config-var@)

#Samesyntaxasfind_package

find_dependency(MYDEPREQUIRED)

#Anyextrasetup

#Addthetargetsfile

include("${CMAKE_CURRENT_LIST_DIR}/MyLibTargets.cmake")

Now,youcanuseconfigurefile(ifyouuseda.infile)andtheninstalltheresultingfile.Sincewe'vemadeaConfigVersionfile,thisisagoodplacetoinstallittoo.

Installing

57

configure_file(MyLibConfig.cmake.inMyLibConfig.cmake@ONLY)

install(FILES"${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake"

"${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake"

DESTINATIONlib/cmake/MyLib

)

That'sit!Nowonceyouinstallapackage,therewillbefilesinlib/cmake/MyLibthatCMakewillsearchfor(specifically,MyLibConfig.cmakeandMyLibConfigVersion.cmake),andthetargetsfilethatconfigusesshouldbethereaswell.

WhenCMakesearchesforapackage,itwilllookinthecurrentinstallprefixandseveralstandardplaces.Youcanalsoaddthistoyoursearchpathmanually,includingMyLib_PATH,andCMakegivestheusernicehelpoutputiftheconfigurefileisnotfound.

Installing

58

Exporting

ThedefaultbehaviorforexportingchangedinCMake3.15.Sincechangingfilesinauser'shomedirectoryisconsidered"surprising"(anditis,whichiswhythischapterexists),itisnolongerthedefaultbehavior.IfyousetaminimumormaximumCMakeversionof3.15orbetter,thiswillnolongerhappenunlessyousetCMAKE_EXPORT_PACKAGE_REGISTRY,asmentionedbelow.

Therearethreewaystoaccessaprojectfromanotherproject:subdirectory,exportedbuilddirectories,andinstalling.Tousethebuilddirectoryofoneprojectinanotherproject,youwillneedtoexporttargets.Exportingtargetsisneededforaproperinstall,allowingthebuilddirectorytobeusedaswellisjusttwoaddedlines.ItisnotgenerallyawaytoworkthatIwouldrecommend,butcanbeusefulfordevelopmentandaswaytopreparetheinstallationprocedurediscussedlater.

Youshouldmakeanexportset,probablyneartheendofyourmainCMakeLists.txt:

export(TARGETSMyLib1MyLib2NAMESPACEMyLib::FILEMyLibTargets.cmake)

Thisputsthetargetsyouhavelistedintoafileinthebuilddirectory,andoptionallyprefixesthemwithanamespace.Now,toallowCMaketofindthispackage,exportthepackageintothe$HOME/.cmake/packagesfolder:

set(CMAKE_EXPORT_PACKAGE_REGISTRYON)

export(PACKAGEMyLib)

Now,ifyoufind_package(MyLib),CMakecanfindthebuildfolder.LookatthegeneratedMyLibTargets.cmakefiletohelpyouunderstandexactlywhatiscreated;it'sjustanormalCMakefile,withtheexportedtargets.

Notethatthere'sadownside:ifyouhaveimporteddependencies,theywillneedtobeimportedbeforeyoufind_package.Thatwillbefixedinthenextmethod.

Exporting

59

PackagingTherearetwowaystoinstructCMaketobuildyourpackage;oneistouseaCPackConfig.cmakefile,andtheotheristointegratetheCPackvariablesintoyourCMakeLists.txtfile.Sinceyouwantvariablesfromyourmainbuildtobeincluded,likeversionnumber,you'llwanttomakeaconfigurefileifyougothatroute.I'llshowyoutheintegratedversion:

#Packagingsupport

set(CPACK_PACKAGE_VENDOR"Vendorname")

set(CPACK_PACKAGE_DESCRIPTION_SUMMARY"Somesummary")

set(CPACK_PACKAGE_VERSION_MAJOR${PROJECT_VERSION_MAJOR})

set(CPACK_PACKAGE_VERSION_MINOR${PROJECT_VERSION_MINOR})

set(CPACK_PACKAGE_VERSION_PATCH${PROJECT_VERSION_PATCH})

set(CPACK_RESOURCE_FILE_LICENSE"${CMAKE_CURRENT_SOURCE_DIR}/LICENCE")

set(CPACK_RESOURCE_FILE_README"${CMAKE_CURRENT_SOURCE_DIR}/README.md")

Thesearethemostcommonvariablesyou'llneedtomakeabinarypackage.AbinarypackageusestheinstallmechanismofCMake,soanythingthatisinstalledwillbepresent.

Youcanalsomakeasourcepackage.YoushouldsetCMAKE_SOUCE_IGNORE_FILEStoregularexpressionsthatensureyoudon'tpickupanyextrafiles(likethebuilddirectoryorgitdetails);otherwisemakepackage_sourcewillbundleupliterallyeverythinginthesourcedirectory.Youcanalsosetthesourcegeneratortomakeyourfavoritetypesoffilesforsourcepackages:

set(CPACK_SOURCE_GENERATOR"TGZ;ZIP")

set(CPACK_SOURCE_IGNORE_FILES

/.git

/dist

/.*build.*

/\\\\.DS_Store

)

NotethatthiswillnotworkonWindows,butthegeneratedsourcepackagesworkonWindows.

Finally,youneedtoincludetheCPackmodule:

include(CPack)

Packaging

60

FindingPackageTherearetwowaystofindpackagesinCMake.

Lookingforlibraries

61

CUDA(inprogress)CUDAsupportisavailableintwoflavors.Thenewmethod,introducedinCMake3.8(3.9forWindows),willbewhatIfocusonfirst.Theoldmethodwillbecoveredafterwards,butasyou'llsee,it'suglierandhardertogetright.I'dsticktorequiringCMake3.8or3.9forCUDA(andCMake3.11forIDEslikeXcodeandVisualStudio).

AgoodresourceforCUDAandModernCMakeisthistalkbyCMakedeveloperRobertMaynardatGTC2017.

Method1:CUDAasaFirstClassLanguage

Thismethodisquitenew,anddoesn'tseemtohavemuchdocumentation.Thereareseveralissuesyouneedtowatchoutforwhenusingit,butoverallisshouldbeamuchnicerandcleanerwaytouseCUDA.

AddingtheCUDALanguage

TherearetwowaystoenableCUDAsupport.IfCUDAisnotoptional:

project(MY_PROJECTLANGUAGESCUDACXX)

You'llprobablywantCXXlistedherealso.And,ifCUDAisoptional,you'llwanttoputthisinsomewhereconditionally:

enable_language(CUDA)

TochecktoseeifCUDAisavailable,useCheckLanuage:

include(CheckLanguage)

check_language(CUDA)

YoucanchecktheversionoftheNVCCtoolkitwithCMAKE_CUDA_COMPILER_VERSION(fornow,onlyNVCCissupported,butjusttobesure,checkCMAKE_CUDA_COMPILER_IDSTREQUAL"NVIDIA").

VariablesforCUDA

ManyvariableswithCXXinthenamehaveaCUDAversionwithCUDAinstead.Forexample,tosettheC++standardrequiredforCUDA,

if(NOTDEFINEDCMAKE_CUDA_STANDARD)

set(CMAKE_CUDA_STANDARD11)

set(CMAKE_CUDA_STANDARD_REQUIREDON)

endif()

Addingalibrary

Thisistheeasypart;aslongasyouuse.cuforCUDAfiles,youcanjustaddlibrarieslikeyounormallywould.

Youcanalsouseseparablecompilation:

set_target_properties(mylibPROPERTIES

CUDA_SEPERABLE_COMPILATIONON)

YoucanalsodirecltymakeaPTXfilewiththeCUDA_PTX_COMPILATIONproperty.

CUDA

62

Workingwithtargets

UsingtargetsshouldworksimilarlytoCXX,butthere'saproblem.Ifyouincludeatargetthatincludescompileroptions(flags),mostofthetime,theoptionswillnotbeprotectedbythecorrectincludes(andthechancesofthemhavingthecorrectCUDAwrapperisevensmaller).Here'swhatacorrectcompileroptionslineshouldlooklike:

"$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:-fopenmp>$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>:-Xcompiler=-fopenmp>"

However,ifyouusingalmostanyfind_package,andusingtheModernCMakemethodsoftargetsandinheritance,everythingwillbreak.I'velearnedthatthehardway.

Fornow,here'saprettyreasonablesolution,aslongasyouknowtheun-aliasedtargetname.It'safunctionthatwillfixaC++onlytargetbywrappingtheflagsifusingaCUDAcompiler:

function(CUDA_CONVERT_FLAGSEXISTING_TARGET)

get_property(old_flagsTARGET${EXISTING_TARGET}PROPERTYINTERFACE_COMPILE_OPTIONS)

if(NOT"${old_flags}"STREQUAL"")

string(REPLACE";"","CUDA_flags"${old_flags}")

set_property(TARGET${EXISTING_TARGET}PROPERTYINTERFACE_COMPILE_OPTIONS

"$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:${old_flags}>$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>:-Xcompile

r=${CUDA_flags}>"

)

endif()

endfunction()

Usefulvariables

CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES:Placeforbuilt-inThrust,etcCMAKE_CUDA_COMPILER:NVCCwithlocation

NotethatFindCUDAisdeprecated,butfornow,thefollowingfunctionsrequireFindCUDA:

CUDAversionchecks/pickingaversionArchitecturedetection(Note:3.12fixesthispartially)LinkingtoCUDAlibrariesfromnon-.cufiles

Method2:FindCUDAIfyouwanttosupportanolderversionofCMake,IrecommendatleastincludingtheFindCUDAfromCMakeversion3.9inyourcmakefolder(seetheCLIUtilsgithuborganizationforagitrepository).You'llwanttwofeaturesthatwereadded:CUDA_LINK_LIBRARIES_KEYWORDandcuda_select_nvcc_arch_flags,alongwiththenewerarchitecturesandCUDAversions.

TousetheoldCUDAsupport,youusefind_package:

find_package(CUDA7.0REQUIRED)

message(STATUS"FoundCUDA${CUDA_VERSION_STRING}at${CUDA_TOOLKIT_ROOT_DIR}")

YoucancontroltheCUDAflagswithCUDA_NVCC_FLAGS(listappend)andyoucancontrolseparablecompilationwithCUDA_SEPARABLE_COMPILATION.You'llalsowanttomakesureCUDAplaysniceandaddskeywordstothetargets(CMake3.9+):

set(CUDA_LINK_LIBRARIES_KEYWORDPUBLIC)

You'llalsomightwanttoallowausertocheckforthearchflagsoftheircurrenthardware:

cuda_select_nvcc_arch_flags(ARCH_FLAGS)#optionalargumentforarchtoadd

CUDA

63

CUDA

64

OpenMPOpenMPsupportwasdrasticallyimprovedinCMake3.9+.TheModern(TM)waytoaddOpenMPtoatargetis:

find_package(OpenMP)

if(OpenMP_CXX_FOUND)

target_link_libraries(MyTargetPUBLICOpenMP::OpenMP_CXX)

endif()

Thisnotonlyiscleanerthantheoldmethod,itwillalsocorrectlysetthelibrarylinklinedifferentlyfromthecompilelineifneeded.InCMake3.12+,thiswillevensupportOpenMPonmacOS(ifthelibraryisavailable,suchaswithbrewinstalllibomp).However,ifyouneedtosupportolderCMake,thefollowingworksonCMake3.1+:

#ForCMake<3.9,weneedtomakethetargetourselves

if(NOTTARGETOpenMP::OpenMP_CXX)

find_package(ThreadsREQUIRED)

add_library(OpenMP::OpenMP_CXXIMPORTEDINTERFACE)

set_property(TARGETOpenMP::OpenMP_CXX

PROPERTYINTERFACE_COMPILE_OPTIONS${OpenMP_CXX_FLAGS})

#Onlyworksifthesameflagispassedtothelinker;useCMake3.9+otherwise(Intel,AppleClang)

set_property(TARGETOpenMP::OpenMP_CXX

PROPERTYINTERFACE_LINK_LIBRARIES${OpenMP_CXX_FLAGS}Threads::Threads)

endif()

target_link_libraries(MyTargetPUBLICOpenMP::OpenMP_CXX)

Warning:CMake<3.4hasabugintheThreadspackagethatrequiresyoutohavetheClanguageenabled.

OpenMP

65

BoostlibraryTheBoostlibraryisincludedinthefindpackagesthatCMakeprovides,butithasacoupleofodditiesinhowitworks.SeeFindBoostforafulldescription;thiswilljustgiveaquickoverviewandprovidearecipe.BesuretocheckthepagefortheminimumrequiredversionofCMakeyouareusingandseewhatoptionsyouhave.

First,youcancustomizethebehavioroftheBoostlibrariesselectedusingasetofvariablesthatyousetbeforesearchingforBoost.Thereareagrowingnumberofsettings,butherearethethreemostcommonones:

set(Boost_USE_STATIC_LIBSOFF)

set(Boost_USE_MULTITHREADEDON)

set(Boost_USE_STATIC_RUNTIMEOFF)

InCMake3.5,importedtargetswereadded.Thesetargetshandledependenciesforyouaswell,sotheyareaverynicewaytoaddBoostlibraries.However,CMakehasthedependencyinformationbakedintoitforallknownversionsofBoost,soCMakemustbenewerthanBoostforthesetowork.Inarecentmergerequest,CMakestartedassumingthatthedependenciesholdfromthelastversionitknowsabout,andwillusethat(alongwithgivingawarning).ThisfunctionalitywasbackportedintoCMake3.9.

TheimporttargetsareintheBoost::namespace.Boost::boostistheheaderonlypart.Theothercompiledlibrariesareavailable,andincludedependenciesasneeded.

HereisanexampleforusingtheBoost::filesystemlibrary:

set(Boost_USE_STATIC_LIBSOFF)

set(Boost_USE_MULTITHREADEDON)

set(Boost_USE_STATIC_RUNTIMEOFF)

find_package(Boost1.50REQUIREDCOMPONENTSfilesystem)

message(STATUS"Boostversion:${Boost_VERSION}")

#ThisisneededifyourBoostversionisnewerthanyourCMakeversion

#orifyouhaveanoldversionofCMake(<3.5)

if(NOTTARGETBoost::filesystem)

add_library(Boost::filesystemIMPORTEDINTERFACE)

set_property(TARGETBoost::filesystemPROPERTY

INTERFACE_INCLUDE_DIRECTORIES${Boost_INCLUDE_DIR})

set_property(TARGETBoost::filesystemPROPERTY

INTERFACE_LINK_LIBRARIES${Boost_LIBRARIES})

endif()

target_link_libraries(MyExeOrLibraryPUBLICBoost::filesystem)

Boost

66

MPIToaddMPI,likeOpenMP,you'llbebestoffwithCMake3.9+.

find_package(MPIREQUIRED)

message(STATUS"Run:${MPIEXEC}${MPIEXEC_NUMPROC_FLAG}${MPIEXEC_MAX_NUMPROCS}${MPIEXEC_PREFLAGS}EXECUTABLE${MPIEXEC_POSTF

LAGS}ARGS")

target_link_libraries(MyTargetPUBLICMPI::MPI_CXX)

However,youcanimitatethisonCMake3.1+with:

find_package(MPIREQUIRED)

#ForsupportingCMake<3.9:

if(NOTTARGETMPI::MPI_CXX)

add_library(MPI::MPI_CXXIMPORTEDINTERFACE)

set_property(TARGETMPI::MPI_CXX

PROPERTYINTERFACE_COMPILE_OPTIONS${MPI_CXX_COMPILE_FLAGS})

set_property(TARGETMPI::MPI_CXX

PROPERTYINTERFACE_INCLUDE_DIRECTORIES"${MPI_CXX_INCLUDE_PATH}")

set_property(TARGETMPI::MPI_CXX

PROPERTYINTERFACE_LINK_LIBRARIES${MPI_CXX_LINK_FLAGS}${MPI_CXX_LIBRARIES})

endif()

message(STATUS"Run:${MPIEXEC}${MPIEXEC_NUMPROC_FLAG}${MPIEXEC_MAX_NUMPROCS}${MPIEXEC_PREFLAGS}EXECUTABLE${MPIEXEC_POSTF

LAGS}ARGS")

target_link_libraries(MyTargetPUBLICMPI::MPI_CXX)

MPI

67

ROOTROOTisaC++ToolkitforHighEnergyPhysics.Itishuge.TherearereallyalotofwaystouseitinCMake,thoughmany/mostoftheexamplesyou'llfindareprobablywrong.Here'smyrecommendation.

Mostimportantly,therearelotsofimprovementsinCMakesupportinmorerecentversionsofROOT-Using6.16+ismuch,mucheasier!Ifyoureallymustsupport6.14orearlier,seethesectionattheend.

FindingROOT

ROOT6.10+supportsconfigfilediscovery,soyoucanjustdo:

find_package(ROOT6.16CONFIGREQUIRED)

toattempttofindROOT.Ifyoudon'thaveyourpathssetup,youcanpass-DROOT_DIR=$ROOTSYS/cmaketofindROOT.(But,really,youshouldsourcethisroot.sh).

Therightway(Targets)

ROOT6.12andearlierdonotaddtheincludedirectoryforimportedtargets.ROOT6.14+hascorrectedthiserror,andrequiredtargetpropertieshavebeengettingbetter.Thismethodisrapidlybecomingeasiertouse(seetheexampleattheendofthispagefortheolderROOTdetails).

Tolink,justpickthelibrariesyouwanttouse:

add_executable(RootSimpleExampleSimpleExample.cxx)

target_link_libraries(RootSimpleExamplePUBLICROOT::Physics)

Ifyou'dliketoseethedefaultlist,runroot-config--libsonthecommandline.InHomebrewROOT6.18thiswouldbe:

ROOT::Core

ROOT::Gpad

ROOT::Graf3d

ROOT::Graf

ROOT::Hist

ROOT::Imt

ROOT::MathCore

ROOT::Matrix

ROOT::MultiProc

ROOT::Net

ROOT::Physics

ROOT::Postscript

ROOT::RIO

ROOT::ROOTDataFrame

ROOT::ROOTVecOps

ROOT::Rint

ROOT::Thread

ROOT::TreePlayer

ROOT::Tree

ROOT

68

Theoldglobalway

ROOTprovidesautilitytosetupaROOTproject,whichyoucanactivateusinginclude("${ROOT_USE_FILE}").Thiswillautomaticallymakeuglydirectorylevelandglobalvariablesforyou.Itwillsaveyoualittletimesettingup,andwillwastemassiveamountsoftimelaterifyoutrytodoanythingtricky.Aslongasyouaren'tmakingalibrary,it'sprobablyfineforsimplescripts.Includesandflagsaresetglobally,butyou'llstillneedtolinkto${ROOT_LIBRARIES}yourself,alongwithpossiblyROOT_EXE_LINKER_FLAGS(Youwillhavetoseparate_argumentsfirstbeforelinkingoryouwillgetanerroriftherearemultipleflags,likeonmacOS).Also,before6.16,youhavetomanuallyfixabuginthespacing.

Here'swhatitwouldlooklike:

#Setsupglobalsettings

include("${ROOT_USE_FILE}")

#ThisisrequiredforROOT<6.16

#string(REPLACE"-L""-L"ROOT_EXE_LINKER_FLAGS"${ROOT_EXE_LINKER_FLAGS}")

#Thisisrequiredonifthereismorethanoneflag(likeonmacOS)

separate_arguments(ROOT_EXE_LINKER_FLAGS)

add_executable(RootUseFileExampleSimpleExample.cxx)

target_link_libraries(RootUseFileExamplePUBLIC${ROOT_LIBRARIES}${ROOT_EXE_LINKER_FLAGS})

ComponentsFindROOTallowsyoutospecifycomponents.Itwilladdanythingyoulistto${ROOT_LIBRARIES},soyoumightwanttobuildyourowntargetusingthattoavoidlistingthecomponentstwice.Thisdidnotsolvedependencies;itwasanerrortolistRooFitbutnotRooFitCore.IfyoulinktoROOT::RooFitinsteadof${ROOT_LIBRARIES},thenRooFitCoreisnotrequired.

DictionarygenerationDictionarygenerationisROOT'swayofworkingaroundthemissingreflectionfeatureinC++.ItallowsROOTtolearnthedetailsofyourclasssoitcansaveit,showmethodsintheClinginterpreter,etc.You'llneedthreethingsinyoursourcecodetomakeitworkforclasses:

YourclassdefinitionshouldendwithClassDef(MyClassName,1)YourclassimplementationshouldhaveClassImp(MyClassName)initYoushouldhaveafilewithanamethatendswithLinkDef.h

TheLinkDef.hfilefollowsaspecificformulaandtellsROOTwhatpartstogeneratedictionariesfor.

Togenerate,youshouldincludethefollowinginyourCMakeLists:

include("${ROOT_DIR}/modules/RootNewMacros.cmake")

#UncommentforROOTversionsthan6.16

#Theybreakifnothingisintheglobalincludelist!

#include_directories(ROOT_BUG)

ThesecondlineisduetoabugintheNewMacrosfilethatcausesdictionarygenerationtofailifthereisnotatleastoneglobalincludedirectoryoraincfolder.HereI'mincludinganon-existentdirectoryjusttomakeitwork.ThereisnoROOT_BUGdirectory.

Togenerateafile:

root_generate_dictionary(G__ExampleExample.hLINKDEFExampleLinkDef.h)

ROOT

69

Thefinalargument,listedafterLINKDEF,musthaveanamethatendsinLinkDef.h.Thiscommandwillcreatethreefiles.IfyoustartedoutputnamewithG__,thatwillberemovedfromthename,otherwiseitwillusethenamegiven;thismustmatchthefinaloutputlibrarynameyouwillsoonbecreating.Assumingthisis${NAME}:

${NAME}.cxx:Thisfileshouldbeincludedinyoursourceswhenyoumakethelibrary.lib{NAME}.rootmap(G__prefixremoved):Therootmapfileinplaintextlib{NAME}_rdict.pcm(G__prefixremoved):AROOTfile

Thefinaltwooutputfilesmustsitnexttothelibraryoutput.ThisisdonebycheckingCMAKE_LIBRARY_OUTPUT_DIRECTORY(itwillnotpickuplocaltargetsettings).Ifyouhavealibdirsetbutyoudon'thave(global)installlocationsset,you'llalsoneedtosetARG_NOINSTALLtoTRUE.

UsingOldROOTIfyoureallyhavetouseolderROOT,you'llneedsomethinglikethis:

#ROOTtargetsaremissingincludesandflagsinROOT6.10and6.12

set_property(TARGETROOT::CorePROPERTY

INTERFACE_INCLUDE_DIRECTORIES"${ROOT_INCLUDE_DIRS}")

#EarlyROOTdoesnotincludetheflagsrequiredontargets

add_library(ROOT::Flags_CXXIMPORTEDINTERFACE)

#ROOT6.14andearlierhaveaspacingbuginthelinkerflags

string(REPLACE"-L""-L"ROOT_EXE_LINKER_FLAGS"${ROOT_EXE_LINKER_FLAGS}")

#FixforROOT_CXX_FLAGSnotactuallybeingaCMakelist

separate_arguments(ROOT_CXX_FLAGS)

set_property(TARGETROOT::Flags_CXXAPPENDPROPERTY

INTERFACE_COMPILE_OPTIONS${ROOT_CXX_FLAGS})

#Adddefinitions

separate_arguments(ROOT_DEFINITIONS)

foreach(_flag${ROOT_EXE_LINKER_FLAG_LIST})

#Remove-Dor/Difpresent

string(REGEXREPLACE[=[^[-//]D]=]""_flag${_flag})

set_property(TARGETROOT::FlagsAPPENDPROPERTYINTERFACE_LINK_LIBRARIES${_flag})

endforeach()

#Thisalsofixesabuginthelinkerflags

separate_arguments(ROOT_EXE_LINKER_FLAGS)

set_property(TARGETROOT::Flags_CXXAPPENDPROPERTY

INTERFACE_LINK_LIBRARIES${ROOT_EXE_LINKER_FLAGS})

#MakesureyoulinkwithROOT::Flags_CXXtoo!

ROOT

70

ASimpleROOTProjectThisisaminimalexampleofaROOTprojectusingtheUseFilesystemandwithoutadictionary.

examples/root-usefile/CMakeLists.txt

cmake_minimum_required(VERSION3.1...3.16)

project(RootUseFileExampleLANGUAGESCXX)

find_package(ROOT6.16CONFIGREQUIRED)

#Setsupglobalsettings

include("${ROOT_USE_FILE}")

#ThisisrequiredforROOT<6.16

#string(REPLACE"-L""-L"ROOT_EXE_LINKER_FLAGS"${ROOT_EXE_LINKER_FLAGS}")

#Thisisrequiredonifthereismorethanoneflag(likeonmacOS)

separate_arguments(ROOT_EXE_LINKER_FLAGS)

add_executable(RootUseFileExampleSimpleExample.cxx)

target_link_libraries(RootUseFileExamplePUBLIC${ROOT_LIBRARIES}${ROOT_EXE_LINKER_FLAGS})

examples/root-usefile/SimpleExample.cxx

#include<TLorentzVector.h>

intmain(){

TLorentzVectorv(1,2,3,4);

v.Print();

return0;

}

UseFileExample

71

ASimpleROOTProjectThisisaminimalexampleofaROOTprojectusingthetargetsystemandwithoutadictionary.

examples/root-simple/CMakeLists.txt

cmake_minimum_required(VERSION3.1...3.16)

project(RootSimpleExampleLANGUAGESCXX)

#FindingtheROOTpackage

find_package(ROOT6.16CONFIGREQUIRED)

#AddinganexecutableprogramandlinkingtoneededROOTlibraries

add_executable(RootSimpleExampleSimpleExample.cxx)

target_link_libraries(RootSimpleExamplePUBLICROOT::Physics)

examples/root-simple/SimpleExample.cxx

#include<TLorentzVector.h>

intmain(){

TLorentzVectorv(1,2,3,4);

v.Print();

return0;

}

SimpleExample

72

DictionaryExampleThisisanexampleofbuildingamodulethatincludesadictionaryinCMake.InsteadofusingtheROOTsuggestedflags,wewillmanuallyaddthreadingviafind_package,whichistheonlyimportantflaginthelistonmostsystems.

examples/root-dict/CMakeLists.txt

cmake_minimum_required(VERSION3.4...3.16)

project(RootDictExampleLANGUAGESCXX)

set(CMAKE_CXX_STANDARD11)

set(CMAKE_CXX_STANDARD_REQUIREDON)

set(CMAKE_CXX_EXTENSIONSOFF)

set(CMAKE_PLATFORM_INDEPENDENT_CODEON)

find_package(ROOTCONFIGREQUIRED)

include("${ROOT_DIR}/modules/RootNewMacros.cmake")

root_generate_dictionary(G__DictExampleDictExample.hLINKDEFDictLinkDef.h)

add_library(DictExampleSHAREDDictExample.cxxDictExample.hG__DictExample.cxx)

target_include_directories(DictExamplePUBLIC"${CMAKE_CURRENT_SOURCE_DIR}")

target_link_libraries(DictExamplePUBLICROOT::Core)

SupportingfilesThisisjustasimple-as-possibleclassdefinition,withonemethod:

examples/root-dict/DictExample.cxx

#include"DictExample.h"

Double_tSimple::GetX()const{returnx;}

ClassImp(Simple)

examples/root-dict/DictExample.h

#pragmaonce

#include<TROOT.h>

classSimple{

Double_tx;

public:

Simple():x(2.5){}

Double_tGetX()const;

ClassDef(Simple,1)

};

WeneedaLinkDef.h,aswell.

examples/root-dict/DictLinkDef.h

DictionaryExample

73

//See:https://root.cern.ch/selecting-dictionary-entries-linkdefh

#ifdef__CINT__

#pragmalinkoffallglobals;

#pragmalinkoffallclasses;

#pragmalinkoffallfunctions;

#pragmalinkC++nestedclasses;

#pragmalinkC++classSimple+;

#endif

TestingitThisisanexampleofamacrothatteststhecorrectgenerationfromthefileslistedabove.

examples/root-dict/CheckLoad.C

{

gSystem->Load("libDictExample");

Simples;

cout<<s.GetX()<<endl;

TFile*_file=TFile::Open("tmp.root","RECREATE");

gDirectory->WriteObject(&s,"MyS");

Simple*MyS=nullptr;

gDirectory->GetObject("MyS",MyS);

cout<<MyS->GetX()<<endl;

_file->Close();

}

DictionaryExample

74

Minuit2Minuit2isavailableinstandalonemode,foruseincaseswhereROOTiseithernotavailableornotbuiltwithMinuit2enabled.Thiswillcoverrecommendedusages,aswellassomeaspectsofthedesign.

Usage

Minuit2canbeusedinanyofthestandardCMakeways,eitherfromtheROOTsourceorfromastandalonesourcedistribution:

#CheckforMinuit2inROOTifyouwant

#andthenlinktoROOT::Minuit2instead

add_subdirectory(minuit2)#orroot/math/minuit2

#OR

find_package(Minuit2CONFIG)#Eitherbuildorinstall

target_link_libraries(MyProgramPRIVATEMinuit2::Minuit2)

Development

Minuit2isagoodexampleofpotentialsolutionstotheproblemofintegratingamodern(CMake3.1+)buildintoanexistingframework.

TohandlethetwodifferentCMakesystems,themainCMakeLists.txtdefinescommonoptions,thencallsaStandalone.cmakefileifthisisnotbuildingaspartofROOT.

ThehardestpartintheROOTcaseisthatMinuit2requiresfilesthatareoutsidethemath/minuit2directory.Thiswassolvedbyaddingacopy_standalone.cmakefilewithafunctionthattakesafilenamelistandtheneitherreturnsalistoffilenamesinplaceintheoriginalsource,orcopiesfilesintothelocalsourceandreturnsalistofthenewlocations,orreturnsjustthelistofnewlocationsiftheoriginalsourcedoesnotexist(standalone).

#Copiesfilesintosourcedirectory

cmake/root/math/minuit2-Dminuit2-standalone=ON

#Makes.tar.gzfromsourcedirectory

makepackage_source

#Optional,cleanthesourcedirectory

makepurge

Thisisonlyintendedfordeveloperswantingtoproducesourcepackages-anormaluserdoesnotpassthisoptionandwillnotcreatesourcecopies.

Youcanusemakeinstallormakepackage(binarypackages)withoutaddingthisstandaloneoption,eitherfrominsidetheROOTsourceorfromastandalonepackage.

Minuit2

75