hey everyone welcome back to the channel
today we've got another cmake video
we're going to be creating a library but
not just a simple Target library that
you may be used to if you've worked with
cake before uh but we're actually going
to go through the steps of configuring
and exporting any scripts necessary so
that other developers can easily find
our library and Link against it uh by
just using the cmake ad Library command
you're just creating one uh for within
your build tree but we're going to be
looking at how to set it up so that it
can be built installed and properly
found by other projects the process
isn't crazy it takes a little bit of
setting up but uh we're going to go
through everything step by step and
you'll see that it's not that hard or
convoluted as um it might seem otherwise
so without further Ado let's get into it
the first thing that I want to quickly
show is the actual code that we're going
to be compiling in this case it's this
one function here called do amazing it's
declared inside of this header file here
and it's defined inside the CP P this is
obviously really basic it's not really
doing anything useful clearly but this
is going to be the actual Library code
that we're going to be compiling
obviously in your projects you're going
to have far more things but the
principles that I'm going to be showing
today are going to apply regardless so
uh what we're going to do is we're going
to create a library we're going to
compile this code and then we're going
to export that Library so that it can be
installed and then quickly used by
another project the next thing to show
here is the actual cake list file we're
going to go in order
uh it's pretty straightforward at the
beginning we've got our minimum required
version our project name and the fact
that we're using C++ but the first thing
that uh I want to point out really is
this ad Library call here which sets the
name of our Target so we're creating a
library Target within cake and
specifying what compilation units are a
part of that Target in our case it's
just that one CPP file with the function
definition uh so what cmake will do when
it sees this is it'll actually generate
build information so that this target
can be compiled using these U
compilation units these source
files the next bit of code here is the
set Target properties which takes the
Target that we just created this Library
just above here and we're setting some
properties so that cmate can export some
compilation Target information uh in our
case here we're telling it that it needs
to notify the compiler that we're using
the C++ 20 standard so what cmake is
going to do when it generates the actual
build whether that's make or a visual
studio project or an EXO whatever the
generator is that we're using what cmake
is going to do is it's going to export
whatever Flags need to be passed to our
Target to specify that we're using C++
20 and that the actual standard is
required the next thing to note is this
include here so we've got the include
new installers so that's including a a
module into our cmake list file which is
is simply just providing a bunch of
variables that we're going to be using
so I'll explain this in a second it's
important to understand how targeting
cluee directories works because we're
using something called generator
Expressions to signal to cmake that
depending on the context of which we're
actually working with this Library
whether we're building it or linking to
it from an installed location uh we need
a different path for our includes so in
the case where we're building it we're
using the build interface and we should
have the current Source directory as our
include path what that means is that the
current cmake list file is the current
Source directory in this case that's
just the root of the project that gets
added to the list of include paths when
we're compiling our source files now if
we build then install our library the
structure of the files looks different
we have an include directory which is
where our public headers are ated and
the way that looks is something like
this this is actually I'm skipping ahead
a little bit but this is what that uh
directory structure looks like once we
actually install it on disk and you'll
see inside of the uh include directory
we have useful. which we've copied and
I'll show you how that happens further
in this is the public header that users
of our library are supposed to be able
to find and include into their project
so they can actually call the functions
and use the types that we're exposing
with our library that's why we're
including the cmake install include
directory here which is coming from this
module here so that we can say in the
case where you're using the installed
version this should be the include path
that's available to you the next bit
here is actually uh doing two things one
it's actually configuring our Target and
the actual install paths that are going
to be used when we actually end up
installing it and this is basically
again using the standard install uh
paths that we're imported from this new
installed dur include up here this
module so the these are all the standard
uh for example the the lib directory the
include directory the bin directory
anybody who's worked on a Unix like
system will probably be familiar with
these paths like you might have/ user
local bin or lib or include uh share
right like you have all of these
standard Unix paths which are uh
actually what cmake prefers and it likes
to look into those types of paths when
it's trying to find header files or lip
files so when we actually finish working
on our library and we go to install it
this is what's going to tell cake how to
actually set up all those different
files within our project so that they
can be installed appropriately the next
thing we want to look at is this export
very useful targets bit right here what
that's doing is it's taking this target
that we've created up to this point and
that we've configured with all of this
information and it's creating an export
Target that can be imported by another
part of our project or another project
altogether and the way that this
actually gets used is if you look over
here is uh we're actually going to be
exporting that export Target we're going
to be installing that file uh or rather
that information into this file this
very useful targets. cmake and cmake
based on this namespace qualifier here
is actually going to prefix all of our
targets with very useful colon colon
it's important to include the colons
here because they're a convention that
cake uses to provide further Diagnostics
and information about that import Target
but uh because we specified this
namespace what cic is going to do is
it's going to take this target that
we've configured up until this point our
library and it's going to export all the
relevant information about it into this
very useful targets. cmake it's going to
qualify all of that information all of
those Targets in our case is just one
with very useful colon colon that's the
Nam space and it's going to write that
file this file here it's going to write
it into this destination and that
destination is the standard lib folder
we imported this from our new install do
if you remember up here that's where
that becomes available it's going to
place that file into lib cmake and
another folder called very useful and
I'm going to show you what's actually in
there if we go here what we're going to
end up with is uh and ignore some of the
other files but we're going to end up
with this one targets cmake uh script
which is actually going to contain all
the important information for our
exported Target so that other projects
can import it and make use of it we
skipped over this install files bit uh
because I wanted to explain this first
since it was tied to the part before but
what we're doing is we're actually
taking our useful. and we're installing
that file meaning we're copying that
file from our source directory into our
include directory uh the reason we do
that is because useful. has the
Declaration for our function that we
want us to be able to call so that is
our public include header so in order
for people to actually know about our
function they need to know about our
namespace and the function declaration
itself so uh needless to say they need
to be able to have access to that header
file making it a public header that they
can use so in order for that file to end
up where it needs to be inside the
include directory we have to actually
install it and uh cmake will copy it for
us when we install uh if we had multiple
header files that we needed to expose as
our public headers then we would have
whites space separation here it could be
you know space or new lines or however
you want to list them uh but that would
be all listed under here and as you'll
see inside our include directory this is
the useful. file that we actually end up
installing or copying into this
directory uh which will be available to
users through this install interface
include path so this include path will
be added to to their uh include paths
when they're using the installed library
and our include file will be in there
for them to actually bring into their
project so at this point we've created
our library we've listed our actual
source code we've configured our Target
uh We've copied we've we've told it how
to actually install the public headers
so that users of our library can
actually uh look up our header files but
we haven't actually made this easily
findable so somebody could actually take
our library now and point to our um our
targets. cmake file and import it that
way but that's not the most portable way
to do things what we want to do is we
want to create a configuration file
which tells cake how to actually find
and import our targets and it's a script
that c can use so that when one of our
users uses the find package command
they'll actually be able to find and
look up all the information needed uh
including all dependency information and
all of that stuff for our actual Library
so I'll show you how that works a very
important thing to talk about is how
cmake actually finds things and in the
traditional way cmake has provided These
Fine files uh for hundreds if not more
thousands perhaps I'm not quite sure of
the most common packages that are used
in uh in the cmake world and that's not
very sustainable because they can't
possibly provide fine packages for every
Library out there that users might want
to use there's endless libraries on
GitHub or other platforms that you might
want to bring into your source code and
use uh or or install on your system
rather and use and cmate can't can't be
expected to have a fined script for all
of those so the new way of doing things
is that the actual Library authors
themselves like us are going to be
putting together these config files
these package config files so that they
are actually giving all of the
information that's needed to import
these librar into other projects so that
the cmake team doesn't have to
distribute These Fine packages anywhere
everything is done by convention so
we're just generating our config file
telling cmake where to find the
installed version of our library and
then cmake looks up the configuration
and pulls everything in as needed
ultimately this config file is generated
uh the targets file is generated as you
saw up here um so we don't actually have
to be too specific and explicit about
what we're doing we just have to make
sure that things are named accordingly
and things are installed where they need
to be and uh so let's get into it in
order to produce our config file what we
need to First do is include the cmake
package config helpers module which
gives us access to the configure package
config file function and what that
function does is it takes this input
file which is sort of like a template
and produces this output file which is
our actual configuration file and here
we're signaling where that file is going
to be installed which would end up being
inside of lib cmake very useful and then
we have our uh config file here you can
ignore these for now but this is the
file that'll end up producing and if we
take a look at it if we take a look at
the input first we have this package
init portion here which is just
something that cmake unpacks into some
validation code that it wants at the
beginning of uh of packages and we don't
really need to go over that right now
but it just does some checking to make
sure that things are the way they need
to be in order for this package to
actually get imported the next thing
that we do is we include our targets
file which is the exported information
about our library the cmake library that
we're putting together so all the
include paths and any other details
around our cmake
library and uh we're bringing that in to
the package config so after that we have
this check required components call
which if our library used components if
it was a bigger library that was split
up into various components this would
make sure that the component that is
being asked for by the users of our
library actually exists within our
library now we're not actually doing any
components for this library because it's
just a small demo but if we were making
a larger library and we had components
set up then we would need to have
something like this if we wanted to
provide feedback to our users if they
spelled something incorrectly or
requesting a component that doesn't
exist I'll do a separate video on how to
set that up but I just wanted to put
this in here uh because it's a good
practice to have it anyways in case you
decide to uh actually add components to
your library F dependency portion here
is um something that we're going to
visit later on in the video but just so
you understand why it's there it's a way
for us to signal to Downstream projects
projects that are trying to use our
library that uh specific library is
required for them to have as well so in
our case here I've said said that the
zib library is a public dependency of
our library so that anybody who's trying
to use our library any project that's
trying to link against the library that
we're making must also have zip on their
path installed somewhere on their
computer and cic needs to be able to
find
it of course once we've set all of this
up it's important to actually test it
and make sure we can install our library
so what we're going to do is we're going
to go inside of the visual studio
developer prompt
so in my case I'm using a terminal and
I've got the developer prompt set up
here and I run that and what that does
and I'll just show you what it does
actually real quick is it actually sets
it up with the visual studio developer
tool chain um so that I can actually
compile code and run cmake and all that
stuff now if you're using something like
Ming GW or other compilers uh we we
can't possibly cover every type of tool
chain here but essentially uh if you
have seen cmake and a C++ compiler uh or
a c c compiler depending on what you're
working on but uh you'll need to have
some sort of command prompt so that you
can actually run cmake and cmake and
find your compiler in our case like I
said we're using the visual studio
developer command prompt uh but the
steps are the same regardless even if
you're on Mac OS and Linux and you just
want to install the library somewhere
so uh here we have uh our actual uh our
project so so we're going to go inside
of the cmake library and just list files
and you'll see this is what we saw
earlier we have our source directory we
have our cmakelist.txt uh and the input
file and all that so what we want to do
is we want to run cmake DS for the
source directory so this is going to be
our cmake list directory and that's
going to be cmake library and we're
going to say dasb for our build
directory and I'm going to do this build
directory we're going to say c make
library and we're going to run that and
what cmake is going to now do is it's
going to configure a build for our uh
build config for our uh Library so we're
going to actually be able to compile our
library and uh in this case here um
we're using like I said the visual
studio compiler so it found it and we're
going to be able to compile so if we do
then cmake oops if we do cake-- build
and we specify the build directory right
so now we're actually telling cake to
run the build command and we're going to
hit enter what cake is going to do is
it's actually going to build it and as
you can see here it's going through our
cmake lists it's uh compiling our CPP
file uh it's also generating some build
configuration that the compiler can read
in but we don't need to look at that
because we're working directly with
cmake which is what we want uh and it's
producing this lip file inside of the
debug directory now
you can with Visual Studio at least you
can specify D- config and if I type
release it's actually going to build it
for
release so if we're actually creating a
release version of our library uh let's
go ahead and actually uh let's do that
for the rest of the demonstration we're
going to build a release version which
is not going to include any of the debug
symbol now this Das Das config option
here is only available for visual studio
or uh or the VIS DC
compiler because with uh with cmake
traditionally you have to actually
configure your build differently and
I've done videos you should take a look
at the rest of my videos I'll probably
put one in the description of how to
actually install libraries and there's a
lot more details on uh the various
things but uh essentially you would have
to set up when you're configuring the
build you would have to set up whether
it's a debug or release build and then
when you go to install it it's going to
install either the debug or release uh
version
so in our case we build it for release
one more time for good luck and then
what we can do is uh do
cake-- install and we're going to point
to the build directory and we're going
to say--
config we're installing the release
version and we're going to say SL prefix
we're going to say where we want to
actually install it so in my case I'm
going to put it inside of my S which is
my developer drive sdks and I have a
testing
directory uh over here and I'm going to
hit enter and what cake does is it goes
through all of the things that we talked
about earlier and it installs all of
those files so it's installing our lib
it's uh if you remember so I've already
got it installed so it says up toate but
it's it's trying to install the header
file that you saw it's installing all of
the scripts that we uh that we asked it
to install in order for uh our package
to actually uh exist as a findable
package so of course in order to get
this demonstration to the end we also
want to actually create a little project
that tries to use our library to make
sure that everything I'm showing in this
video is actually applicable so in this
case I've made this little uh project
this application console application
called cmake Library user and what it's
doing is is just importing our header
file our
useful. and uh it's trying to call do a
amazing which should print the word
amazing uh but uh if you work in Visual
Studio or if you're doing this from the
the command line what you would see is
that there's an error in trying to
actually configure this application so
if we actually look at the cmake list
file we'll see that it could not find a
package configuration file provided by
very useful and it's what it's looking
for is one of these types of files what
or a file named in one of these two ways
so if you recall we actually produced
this configuration file and it's uh we
know that it's in there because it is
inside of our lib directory we have this
file right here the very useful config
do cmake which is exactly what cmake is
looking for when we try and do find
package it can't find it because we have
to help cake uh in order so in order for
it to know where it's looking for these
files so if we take a look at the cmake
presets Json file uh and you can do this
from the command line if you do uh cake
DD cmake prefix path equals and you
specify the path or list of paths for uh
where cmake should be looking for these
packages but in our case because we're
actually using visual studio the way
we're doing it is we're going to use the
cmake presets Json file now there's also
another file uh called cmake user
presets where you should really be
putting this stuff and I'll do another
video on that uh uh so look out for that
please subscribe but uh essentially just
to keep this demo moving we're going to
put this in here uh and we're going to
say cake prefix path and this is going
to be the package prefix path where it
should search and we're going to specify
our path right here this is where we
installed the
package and we're going to do this and
because it's Windows um and paths have
this slash we actually have to switch it
to this slash because otherwise it's
going to treated as an escape character
in the
string so now that we've told cmake
where our library is and we click
generate we'll see that we actually have
another error and uh what's happening is
it's we're finding our library we've
built it we've installed it and users
can now find it once we provide this C
cmake prefix path but our library has a
requirement if you recall we've said
that anybody who pulls in our
configuration script needs to have zib
and because we have told our project
here where to find zib installed um now
this wouldn't be an issue on something
like Linux where you might install it as
a system package it'll just find it
automatically but because we're on
Windows and those standard paths don't
exist uh in quite the same way uh we
have to be a little bit more helpful to
cmake so uh we click generate and we see
that it's uh it's actually complaining
about not being able to find zib and I'm
going to actually close that cuz we
don't want to jump around too much but
we need to tell cmake where to find that
and that's easily solved by having ZB
installed in my case I already do and
it's installed in this path right here
and as you'll notice it's actually very
similar to what we're building for our
library uh cmake um is is using the
standard paths here and there's the lib
files there's the include directory that
has the public headers for zib and
anything else there's some doc files
like Man pages and all that but that's
all the stuff that the ZB Library um has
installed on my system so if I go in
here and I actually paste that path oops
and once again we're going to switch
those
slashes and we're going to hit generate
what it's going to do is it's going to
generate our build and it's actually
going to find zib and in this case
everything is configured exactly as
needed so what we needed was our very
useful we found that
library and uh then we're producing an
executable based on these uh source
files that we provided here and we're
actually linking to our library if you
recall the name space that we specified
for our configuration our targets rather
uh if you remember and I'll just quickly
show that again when we created our Expo
our targets like our core configuration
for our library we said that we want all
of our targets to be namespaced with
very useful colon colon and here you see
that in action we're saying that we're
trying to link to our library using very
useful colon colon that's a namespace
and the actual target has got the same
name as the namespace in this case so
what cake is going to do is it's going
to then link our library to the
executable here and if we actually go
ahead and build it we'll see that we're
going to actually
compile or we might not compile because
we're actually using a debug build but
we're actually using an installed
version of our uh uh sorry rather a
release version of our library so uh I'm
going to actually show what we can do
here and it's good that we ran into this
error is we're going to change this to
debug so now we're going to rebuild the
library for debug and we're actually
going to install a debug version of our
library we're going to say
debug and everything should be up to
date now so if we go here and just for
good luck we're going to delete the
cache and reconfigure and make sure
everything still works
cmake is going to reconfigure our
project and if we rebuild we're going to
see that everything is built
successfully and I probably should have
dwell on that error a little bit more
because it's a little bit cryptic uh and
actually let's do that really quickly so
what I've done is I've reinstalled the
release version of our library and I've
rebuilt our our test project here just
to see that err and without really
looking at every little bit here we can
quickly see that there's mismatch errors
in the Linker phase
and what that tells me automatically is
that there's probably some mismatch
between the what we're actually building
for in this case a release build and uh
one of our dependencies that we're
linking into our executable we're
statically linking our library into this
program here uh and there seems to be a
mismatch between what the Linker is
getting and what the Linker is expecting
then we can build and that eror should
go away again and that's it now if we
actually run this program
uh we'll see that we get amazing which
is exactly what we expected so our
program is working correctly and uh
we're able to call into our
code thanks everyone for watching I hope
you found this video useful I hope it
leads to more people understanding how
to create sharable and easily usable
code so we can all take advantage of
each other's work uh if you found this
educational and helpful please consider
subscribing and definitely hit that like
button I appreciate it thank you