Better CMake Part 4 -- find_package() Basics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
you guessed it we're back in the topic of cmake roll the intro you just gotta create a branch and get on with it this series has been a collection of topics about cmake to make it a tool that goes from working against you into one that works for you for many of us find package in cmake is a command that's big and scary now i hope in today's video that we can demystify it a little bit and turn it into a powerful tool you can wield in your projects cmake has the ability to go find packages out on the system and import them into your project so you can use them the vast majority of the time this ends up being libraries but other executables and resources can be included too most of the discussion today will be about libraries but just know that the concepts will generalize they're universal so here we are looking at some code i'm gonna look at find package now to get started maybe it's good to say a few words on what's the point why are we looking at find package at all and it's to get targets that are outside my project imported into my project and then you can use them so we're going to look through this little example right now which is a little kind of hello world program i i work on a project called osprey it's a rendering library here's a little hello world program of osprey that sets up a little programmatic mesh the details of that aren't super important the key is that if i have osprey somewhere on my system installed that i can go get it make sure a particular version requirement is met and if so cmake will succeed and give me this target now there's a lot of details for how projects that export a target and we're going to go over that in a later video not today today we're going to look at consuming packages that are found on the system and so last uh my last video in the series talked about what targets are so this executable target's the only one we're building in this example uh and there's different properties that come with targets and so the idea is there can be those same properties attached to libraries and other resources on the system that come into existence uh for this app called osp tutorial to build against uh and that's what this target does so like all things in cmake and cpas plus and programming in general it's good to look at the manual and look at examples to to learn how they work so i am a big fan of kit ware's documentation of cmake so we can have just a little look at the find package signature and what's great is the the latest documentation splits into a simplified like most common use case to get kind of the this is the most of the time what you're going to encounter and then there's the here's the actual interface of everything fine package can do and it's a ton of stuff and so we're not gonna cover all those details today the idea is with the basic usage you can get a lot work done and then once you have all of that you can start to explore the uh the more complicated stuff and again not get too attached to the complicated stuff but you know when there are those times that you have some crazy setup that you need a fine package probably can can do some stuff for you which is great so the way find package works is it's it's a command like you saw on the example code that you have a particular package you're interested in and then everything after that is optional so this is the only required argument you can provide a version and say if it needs to be exact or not so you saw there was osprey 2.0.0 what that'll be is as long as the major version is the same then you'll need that minimum version or grader for this fine package to succeed so if it finds something that's older it will fail or if you provide exact if it doesn't exactly match this version it'll also fail required says if this fails cmake fails so there's plenty of places where you find package and if you can't find something on the system maybe some feature gets turned off where you don't build with that dependency and so required says if this fine package fails it is fatal to cmake being able to succeed on the project you're trying to build and quiet is kind of like the inverse but not quite so it's it's optional quiet says it's optional and uh if it fails we're not going to have any output at all and this is a common option when if you have maybe a copy of a dependency or you want to use fetch content or something to satisfy a dependency but you would rather prefer the system first what you can do is find package quiet determine hey did this was the thing found that i was interested in you know package name and then make decisions off that and you don't need to muddy up the cmake output um with hey this failed what's going on this this will just transparently um do that behind the scenes and then your cma code can still make decisions on if this was found or not we're going to talk about finding configs versus modules and so modules attached to that i guess the other thing i'll mention about components in general is some packages depending on whether they're modules for how do you teach cmake how to go find something that's what a module is is you say hey cmake this is how you go find dependency x sometimes modules or even configs will have be subdivided into smaller components so like cute and boost another really large projects so it'd be like i want to find boost well what parts of boost do you care about because there's a lot of libraries within boost components is a way to let a package subdivide itself without making you do lots of individual fine packages so you could list them all at once and then of course all of these things apply to components as well so this documentation says a lot of that so hopefully if if that's somewhat making sense to you everything that i just mentioned then hopefully that'll make this a little easier to read so the next thing is to distinguish between an imported target versus a normal target by convention and by uh cmake really encouraging it a modern cmake i'll say in cmak3 that there's this this is still just a single string um but this the namespace syntax is a way for cmake to know oh this package exported this target and so this convention helps commands like targeting libraries know that this should have been imported because remember back in the video that talked about targets target link libraries can take raw library files on your system and so it may not know that this is a target like if if it was just this this actually might be osprey.so and then cmake will try to put that on the link line but this namespace syntax indicates that this was imported so if like i didn't have required here and this failed uh this is a way that cma could be like looks like there was an imported target that didn't get satisfied is are you sure that's what you wanted so that's a that's an error a warning message you'll see sometimes but an imported target is one that is different because this this target name cmake knows doesn't have to be built somewhere else in this cmake tree and so that's but otherwise it's just a target that behaves like other targets that we build here locally like this executable target or if we had some directory that built a little shared or static library um that would be a target and then we would link it here so this is all fine and dandy however find packages when it fails that's when people kind of get nervous and there's there's two concepts for us to consider there's find package configs and find package modules so we'll start with configs now if i open up a terminal here somewhere on my machine i have a custom installation of osprey and all of its dependencies in one directory called render kit and i usually do this in home opt so if i go to i have some some stuff installed on my machine here this is just in my home directory this is not standard so this is not um this is not user local this is not slash user that's like what your package manager is doing but for me my setup of my machine this is what i like to do so find package is going to look in default places like user user local by default on windows i forget what the rules are you can go hook it up in the documentation i'm a linux guy um but the point being is the standard locations cmake knows how to go find stuff so everything else is about hey i want to have find package find something and i need to tell cmake where that is so we'll get there but what are we looking for is the key because the package find package is actually looking for cmake configs not the actual files themselves that's what modules do but with a config i did a make install of of embry open image noise some open vkl stuff osprey stuff there's tvb in here the point being is this is actually multiple projects all installed in one directory in opt render kit under this they there are also some cmake configs which let's go to osprey that there are these named files that are named this very intentionally to say that this package all of its exported information that so we like i built osprey and osprey's build system because it's built with cmake we we have it export certain targets that then downstream projects like this one can then link and so that's that's contained in this osprey config what happens is find package will go and look for this file so if it's in like user local lib cmake or user share cmake like there are some standard paths in that documentation it'll say where it's looking it's still looking for this file just because it's in a non-standard place on my machine doesn't mean that it is inherently any different there's there's multiple ways we can point find package to get to this file name but just know that when you are looking for a project that exports targets this is what we're looking for like all this other stuff in here is it is included in this so this is the thing it's looking for it's package and then capital c config.cmake and this package name exactly even case i think matching what is in find package so all of the render kit projects have this and there's plenty of projects out there that export targets and so uh cmake knows to look uh under lib cmake whether it's on your system or somewhere else and again we'll get to how to point that but just know that's what we're looking for so that means how did we do that we'll get there but modules are then what are different from configs so i'm gonna go over here to my little cycles viewer that i think i used in a previous video uh we can just close all this stuff so we don't need it here's what a module is notice that this find something this is now find and then a package name so if i want i need to find package cycles here and that's capital c cycles to use a fine package module means that hey find package didn't have a can it didn't find a config in any of the places it was looking for so now it's got to go find a module to to say hey can can you is there c make around here somewhere that has taught me cmake how to go find this thing called cycles and uh at the end of the day it's it's kind of similar but um in the result but a very different mechanism so configs are say here are the targets the libraries like executables even you know all those properties associated with those libraries here's the things that here are the targets that come into existence a module is trying to make targets based on library files it finds include headers etc anything else and so yeah i started there was a there's actually a fine package module uh that tangent animation had in their hydra cycles delegate hd cycles and i took it and extended it to create a target because at the end of the day creating a target whether you find a config or use a fine package module you should be making targets and so what i do here is i took all the stuff they did before which was hey we're going to go find some stuff and maybe in a later episode we'll talk about like how to write one of these things like really effectively but we look for some include directories look for some library files um you know we look for some library files look for in particular places from some route anyway at the end of the day there's this light imported library target that gets created and then this this library target carries all the link dependencies the include directories all the compile definitions necessary so that means in the app itself so somewhere there is a target here that links cycles and all of those things the include directories the libraries pre-processor definitions all that stuff is now encapsulated in this target and all of that information is then propagated down local targets i'm building in this project so this little interface library i have that then gets linked into another interface target that then gets linked in the app all of that information is propagated according to those these target link libraries um so what that means is my app because it linked this and this linked cycles um i'm not scrambling here to figure out like do i have the right cycles include directories and the preprocessor definitions and all that no you just you just link a target and that's it and everything propagates um so with this module that's exactly what we do we make sure we find all of the things on the system uh necessary to satisfy that and then there's a standard way to say hey did you find everything and if you did so if we found this is a standard output of again we'll talk about those details in another episode but anyway if we found the things we need to find uh we wrap it all up in a target and then away you go so if we go back to this little example we can talk about hey what happens when this is in a non-standard place well let's go to my build directory and let's just start afresh so let's c make this project there are some warnings because that best stuff's up in an osprey but the point being is how on earth this fine osprey in the right place there's multiple places to do this now i'm going to unset um cmake i'm gonna make sure my magic stuff is out and let's just start again yes and now let's see make this is this should fail yes so this is the message that trips up so many people we did a fine package osprey and in all the standard places on the machine like it didn't show up there was no osprey to be found and this this config is is what we're looking for now it's your project your downstream project so our upstream project to free which direction we're talking about osprey here osprey ideally should export its targets and in the documentation we tell you hey yeah we do this so find package go find our osprey config it's in the standard place under lib cmake osprey and version all that stuff um this we export to you and so um remember there's both modules and configs we don't have locally a module that knows how to go find osprey and so we got a point find package at the config there's multiple ways we can do this it even tells you here how to do it so you can actually take the prefix of osprey and put it on cmake prefix path this is a concatenated path kind of like ld library path can take paths and add non-standard locations to them so if i export grep cmake prefix path nothing there we have we have nothing in my no environment variable set anyway we can we can just add it to prefix path or you can take individual projects and point them individually with a variable which is the package name under bardur which is the directory to where this is both of these things we can set both as environment variables and as cmake variables we can fill out so if we bring that message up again we can find this with a cmake variable we could say cmake-d osprey der equals opt render kit libs c make osprey 2.4.0 and we have found osprey um now we have to go because osprey needs embry it does find dependency embry and we could do this for individual projects or we can use cmake prefix path if i start over and let's say just so you know uh to to finish the last thought you could also export osprey der equals and then the same location but uh c make prefix path is pretty powerful and so what i'm actually going to do is just reset my terminal which gets me to talk about something else which is the environment so i do export grep cmake prefix path this is something because i have in my system my personal like organizational system is to put installation of particular projects in home opt and then name of project um heavily use your bash rc like or z shell rc or whatever your your login config file is for your shell that you use have this set every time one thing i find very interesting people get really annoyed at like oh man i had to specify all these pads well if you're going to do it all the time just put it in your mash rc but cmake prefix path is great because you know you could also have like coal semi or a colon and then more pads so i only need one here but what's nice is let's see if i'm clear here yep so if i run cmake now again the reason this succeeds is because cma prefix path is going to go look for all of those configs but cmake prefix path only needs that lib cmake directory so if i go to opt render kit lib you'll notice there's a cmake directory in lib this is all in that documentation you should read it if you really want to know these details but just know that that each one of these directories is so if i look in ls if i look at embry there's the embry config open image noise is the open image noise config we looked at the osprey one all these configs are in there so like when we say osprey der we're only going to be looking for the osprey we have to have a path to osprey only versus cmake prefix path we'll say well give me the prefix to where all the projects will be and then it knows to like look in a directory that's just osprey and then optionally osprey with version information if we wanted to have more than one version of osprey here installed in under render kit just know that cmake knows how to differentiate all those things and so you can use cmake prefix path to where all of your configs are and this could be cmake prefix path there's multiple places like i could install all these projects in their own standalone directories put all of their cmake their lib cmake directories on cmake prefix path and then i'd have the same output of being able to see make and it knows how to find this stuff it found open vkl found embry found osprey found all this stuff so again that's a config so if i unset cmake prefix path and i go and build all over again uh this config isn't local or sorry this module isn't local and this config needs to be found that's that that's what find package when it fails this is what it's failing on you'll also find that if you had components in here like buying package boost and you had a bunch of boost libraries but you didn't build the right ones you were looking for with find package then of course you get a similar meth looking message but it said hey uh i found these components but i didn't find these components and you told me you had to um but that's that's that's the basics of what we're going for is we're looking f if we don't have a module local that is find osprey um then we gotta have a config and if we don't have a config this either fails or what i could optionally do is this so i could then cmake and it still is now a warning because it's no longer required we still get the same output but it succeeded but then here's that message i was talking about with the missing target because because of that colon colon in there c makes like that's probably not a libra a shared object library or a static library that needs to be added to the link line so this is probably a target and so because this wasn't satisfied this target didn't show up because this failed is this missing yes it is because look it was missed uh so that's the the basic mechanics for how this works you know use cmake prefix path or have individual project underbar ders in your bash rc to let your environment be friendly and again that's all if you want to have non-standard installations mike i'm a big fan of of having things like a walled off op directory here because i could blow away render kit and i don't have to go into like user local lib and figure out what are the individual libraries i can safely remove and all like the headers and like i can just say hey that's my installation route and i can blow it away replace it upgrade it whatever and it's trivial to do that that's my personal workflow um just take that for what it's worth so that's it for today cmake find package has a lot in there going on but if you look at it piece by piece it's not so bad it can be something that's very valuable and helpful if you enjoyed this video hit like and subscribe and let me know down in the comments if there's a topic about cmake you want to hear about there's a lot we could talk about so i'll make sure it's tailored for you out there and until next time happy coding everyone
Info
Channel: Jefferson Amstutz
Views: 4,169
Rating: 4.9649124 out of 5
Keywords:
Id: 1HjAYqcJwV8
Channel Id: undefined
Length: 23min 9sec (1389 seconds)
Published: Fri Oct 23 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.