Makefiles: 95% of what you need to know

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
work with make so I'm gonna be talking about make I'm gonna say some things towards the end that give you context for other build systems because in the end they all do the same thing what our build systems and what are their goals so build systems goals are ATLA very least to kind of organize the magic incantations that you have to use to essentially build your code so late automate the compilation in linking of source code into your final executables right for different systems they may do different things xv6 not only produces the executables but also allows you with a Khemu option to be able to run the virtual machine so you know they can do a lot of other things but at their core they're there to automate a lot of this build process too and that's really important right I mean if you're on a big team of developers you better all be sure that you're building a source code in the same way if a new developer jumps onto a project they need to be able to know how to actually run your code right this is a really really important thing second build systems are layer to make sure that the compilation is only done on things that they need to Rika to compile so you do not need to recompile all code all files when you build a system you actually only need to recompile the files that have been changed or that rely on files that have been changed so build systems are there to automate that process and they're there to actually make the build system easier which is to say when you're actually writing all these instructions for how to actually build your system they aim to be a programming language to make it a little bit easier to actually write that build system so I have created a few examples to motivate build systems and to motivate everything that we're talking about here and I do want to remind you before we go on to lot that that is absolutely the wrong Firefox and I don't not know why it's choosing that hold on one second I mean for some reason it defaulted to the bad Firefox there we go okay I want to remind you that all of this is in a repo for leavening OS hour on my account so I always populate everything before list so if you want to follow along with our anything that we're doing you can do so so you can see the actual rendered markdown for what I'm going through in Emacs is here so just as a brief reminder there okay so the first example is I created what I think a lot of people would do intuitively if they we're told hey somehow you need to organize the magical incantations that build your executables and that is to just generate a build script literally a shell script that includes all of the information that you'd want to have to actually build your program right so let's look at what that looks like so let's go into the build script version of this and I've written a build script that build script just includes a number of commands that literally build my project right so what is my project let's dive into that a little bit first so I have X dot C Y dot C and a header files eh so I have X dot C Y dot C and let's split this not that way oh let's not do any of that do to do do to do that's what I wanted x dot C and z dot h okay yeah let me really see the build is H and SH file is a shell script so it's literally interpreted by your shell to just be a set of commands so in this case it'll execute GCC GCC and GCC and hash-bang at the top just says yeah run this file through the shell right okay so this program is just a few things one in Y dot C we include a single library function Y function and it just returns kind of a dumb value the argument times some constant value and we can see that constant value is defined within Z dot H a header file that is included from Y dot H and it's 42 because that's you know the meaning of life and everything that you'd possibly need we have the prototype for the function fine and then X is kind of our main file it includes an X function that calls the Y function with an argument it includes e dot H so that we can figure out what the prototype of the Y function is and as a main that simply calls the sex function prints out the value and then returns so this is a trivial program nothing's really here right this is not interesting in any way however what I do want to point out is that if either X dot C or Y dot C are changed when I'm building my system I probably need to rebuild that dot C file and then recompile it executable because not only if I generated a new dot o file corresponding to list dot C file but I need to you know compile all the dot o files together to generate the actual output so looking at this running so that you see what I'm talking about we run the build system it executes and all that it did is run all of these commands some of them generate the dot o files so we see that X dot o and Y dot o have been created and then the final GCC command there generates the binary compiles together the dot o files so if I were to modify either of the dot C files you know I absolutely have to rebuild them right however the tricky part is a if I modify the dot H file well I'm also still gonna need to recompile both of my dot C files because they both include dot H they have a dependency on it right this build system all that I've done is I've encoded the commands for building the system into list shell script that's it it has no intelligence about only recompiling specific files if they need to be recompiled right so for instance if I were to take my Y dot C function and change it to be that I want to return a plus 1 times the constant right ok cool now I know I need to recompile my Y dot C but I actually don't need to recompile my X dot C because it didn't get modified the dot o file is gonna be exactly identical to what it was before however of course if I run my build script it runs all of the commands so to show that let's remove all of the files and yeah it's going to run all of the commands regenerate all the dot C files etc right I will answer that asked at the very end after I'm done talking about this ok so this is all very simple I have my being it Jenna it you know runs the code it's fine ok let's go back to what I said build systems are for build systems all to organize magical incantations that you need to build the system cool that's what our build script does however it doesn't only recompile what's necessary to recompile because it's been modified and it certainly doesn't contain anything to make writing the build system easier every single command that I wrote every single command that I had to write within build Sh I kind of have a lot of repetitive stuff here right so I'm repeating myself a lot what happens if now I want to add optimization flags okay now I need to go through an ad Oh three to all of these lines manually right that's not great that's a recipe for forgetting to do it somewhere and having horrible bugs so this is not a great programming environment for a built environment so nobody does list this is not the way that you actually do build systems right so now also if I want to get rid of all of the crap late-- was generated by the system I kind of have to manually go through and do the commands to do it okay that's not great so that's why we have make files because make files allow us to more easily write our build system and they allow us to only conditionally compile what needs to be recompiled because it's been modified so xv6 is a relatively small system if you type make clean and then make it rebuilds every single dot C file and it doesn't pretty quickly but the Linux kernel takes four hours to recompile if you go through everything right so you really only want to have to recompile the dot C files that you changed right so make files try to solve that problem so let's look at a simple make file in this directory so this is very similar to some of the make files that I provided for you for the C programs that were earlier in the class right these are the C programs for the link list and all that stuff right okay so what do we have here first we have the ability to define variables variables allow us to not have to repeat ourselves right so here I'm saying I want to use whatever I've set the CC the compiler variable to and I want to use it again here so I know that whatever my compiler is set to it will be replicated in these two places that means if I want to change this to the LV MCC the LLVM compiler now I just need to change this one line to do so so I don't need to go through my entire program and change that right so variables are useful just like they're useful in programming right likewise my C flags these are all the flags that I end up passing to GCC when I compile a C file as you can see here they're placed on the command line for building the C file if I wanted to now add my optimization flags I've even defined a nice variable called opt for those optimization flags and I can pass three cool now it's getting much faster because I'm optimizing the code even more right I don't need to go through and modify a whole bunch of lines adding Li optimizations manually right I can summarize all the C files in my project in a variable I can summarize all the objects that I wanted to generate for low C files in a variable as well and I can summarize the binary right so variables are intrinsic and make they're important for documenting your build system for documenting your make files and they're important for just making it so that you don't need to repeat yourself okay now the fundamental form that we always abide by Anna make file is this something on the left so this each of these lines and the incantations below them so for instance lists this is a build rule every single rule starts with us : delimited separation of the target the thing that is going to be built in this case all which actually isn't building anything and the dependencies that lat has to be complete and to be able to execute so it's kind of convention that all make files include a all rule or an all directive so that you can type make all and it will build everything in the system it's also convention that you tend to provide a clean as you now know from xv6 right so clean just kind of gets rid of all of the build artifacts within your system right so in this case it removes the binary and all the dot o files that were generated actually this is not using the abstraction that we came up with so it should be that that's cleaner remove the objects we defined a variable for that for a reason right so let's look at this what is this rule within GCC saying it's saying there's a binary that binary ends up being bin which is the output that we actually want to generate it's the final binary that we can actually execute the program that we want to execute right it depends on all of the dot o files having been properly generated in the system so it's saying for this rule this command to execute I absolutely require that all of the dot o files have properly been created within the system so again to the left of the colon if you want to build this then all of these things need to have been created and variables in make you know it's just kind of copying and pasting this stuff there so this is functionally equivalent to just having that right but of course we have variables so why not use them right ok so that's what each of these is saying now let's kind of you mystify light a little bit so what I'm saying here is all if I want to build anything if I want to build it well if I want to build everything then the binary must be prepared oh well okay how's the binary prepared here the binary is prepared because it's the target on the left-hand side of the rule that depends on all the objects having been prepared oh okay is there a rule for creating lis objects it doesn't seem like there is because all of ladakh OHS like there's no X dot Oh rule so I would think that would look like X dot Oh is dependent on for instance X dot C and then I could write some stuff there right and I actually do have that rule it is this rule so this is a make feature where we essentially have wildcards we essentially have regular expressions where the percent sign is a wild card so it's kind of anything right so this is essentially saying if you want to build any text dot o then that depends on that same text dot C so if I want to build X dot oh so there's a dependency on X dot oh well to generate X dot oh that's going to depend on X dot C oh okay so this is actually a generic rule that applies to every single dot C file that will generate a dot o file so when you want to create a dot o this is what does it write and what is this doing it's essentially saying run GCC with the flags that I've set and here we have a couple of make special variables at says whatever is on the left-hand side of local the target just basically take that and copy and paste it there okay the carrot says take whatever is here on the right-hand side of the colon and paste it there okay so that's basically the rule for creating a dot o file so we've said within this role here this is how you generate this is a recipe for generating a generic a file it depends on the dot C file and here's the way that we build that dot o file from the dot C and we're essentially using these to reference the specific target and dependencies above okay so let's see that actually running if I run this I can see that it actually works and I have let me actually run it in here okay and clean make so I can see that I have all of I have one GCC running so I know that that variable is working out okay and then I have a whole bunch of flags that are being passed to the the GCC which is correct I have my GCC and then I have my Flags right and then the rest of it I have my - see that's just getting translated directly I have my - oh that's being translated directly but now I can see it's actually putting the dot o X 0 and X dot C into Lowe's proper Lowe's cute location so it's replacing the dollar sign at with the dot o and the dollar sign carrot with the dot C right ok so if we have three test Falls for example two dot the extension and want only run one how do we do it with this make file so this is running this rule multiple times once for each dot o file that it's asked to generate and that's because we are separately listing our dot o files here so when it sees the dependency over here on X dot o at that point it will look for the rule to actually execute for that and we'll see oh I know how to generate that dot oh I'll do it here right and then it will get till the next dependency y dot o and it will say Oh same rule I can apply this and it will generate my Y dot oh right so that's how this I this actually ends up getting run once for every single C file and that's kind of the beauty here that's the point that I want to make right this is making the build system easier to write because I don't need to write a command for every single dot C file instead I write a command for all dot C files I write a generic recipe right so hopefully that answers the question if not please continue to ask so just a reminder what the @ does here is the dollar sign at is shorthand for the target that is on the left hand side of the colon so whatever is on the left hand side of the colon here gets replaced where this at is essentially right okay so now to clarify also after we build all the objects this is the rule that we use to build all the objects right the objects themselves are not runnable okay so I cannot do it X dot oh that's an object file it's not a executable file let's see what file says so this is now I don't want to go into light okay so after all the objects are created then the dependencies for this binary rule are satisfied so after li objects are created then binary can be generated how do we generate binary by running this command right so what is this running it's saying run GCC and the output of running GCC should be the name of the binary in this case bin and it should take as its arguments all of loes object files so remember dollar sign carat is all of the things on the right-hand side of the colon which in this case is all the objects so what does that look like well we see that we have this G CCO bin x & y dot o so we're actually combining our datos into the binary and the binary is the thing that we can actually run here right so hopefully that explains and taking the explanation all the way through once binary is created then we can actually execute the rest of the rule for all but there actually isn't any code to execute for all that's within the rule so we're done second thing that I want to point out is we have this all rule up top the reason that we kind of by convention have all at the top is because when you just type make it always just by default runs the first rule in the file so we don't actually need to type make all make all it's the same as typing make now notice that when I type make now it actually doesn't build anything so there's nothing getting run here so if I execute my Ben cool my yeah III I don't need to build anything else I don't need to build anything here because I haven't changed any of the files if I don't change any of the C files why would I possibly need to rebuild the binary right so if we have test 1 test 2 deaths test 3 all dot C and we want to execute the binary for only test 1 dot C how do we do it generally you don't have that case you might have if you do have that case you might have a separate directory with a separate make file for each of the binaries that you want to generate this can get more complicated we could have this binary thing we could have it only run on a subset of the files or have multiple lines of code here that generate multiple binaries from different subsets of the objects you know there are many ways that this can happen but in this example we are generating binary from all of the dough's and Nazis I want you to understand not every way that you can use a make file because there are an infinite ways this is a Turing complete language but I want you to understand how make files think and how they function okay so a reminder part of what build systems are allowed to do is to process only what's necessary rebuild only what's necessary right so we're seeing here that when I type me it says there's nothing to do again that's because I haven't modified any of the files if I were to remove the binary well okay now I hope it'll rebuild the binary right oh it does but importantly it only ran the command to rebuild the binary it did not run the command to remake luda files right the dot C the X dot C and the y dot C didn't change so the X dot o and the y dot o do not need to change right so all that we need to do is run the command now to regenerate the binary from the dot o files right make is being very intelligent here and it's essentially saying I will only run this rule on the dot C files that have actually changed within the system so if a dot C file has not changed then it will not run this command again because the dot o file would not mean it would be exactly the same if for to run again right here the binary does not a did not exist anymore so it knows it needs to run this rule again so that's why make was smart enough to only run that second part where we're running the rule to regenerate the binary right okay it's just somebody's confused about the binary here versus the binary here everything this is just an Emacs thing right so everything on the left-hand side of the colon and Emacs is the dark pink because that's a target for the rule the difference between this and this is just this is like a shell script when you want to use a variable you have to you know surround it with dollar sign and parentheses when you want to declare a variable you do not so this is just how you declare variables and how you actually use the variables that's all good question I glossed over lat okay now if I were to go in to my Y dot C file and changeless like I did in the previous example to a plus one so I'm just changing the logic for the Y function right so the dot o the object corresponding list the code generated for less absolutely has to change right so I I hope that this is going to actually rebuild the dot o file for Y dot C and when it does so I hope that it will also rerun the command to generate the binary because Y dot o will have changed okay so when I run make now it says ah the Y dot C let you that you had has now changed you modified that code because of that I need to regenerate the Y dot oh I need to rerun this command to generate the Y dot o and then because the Y dot o has changed it knows that it needs to rerun this command to reproduce the binary so all that make is doing here is it is looking at nope nope nope nope oh it is looking at what has been changed within these dependency lists and it's rerunning the command for Low's and it does it in this case transitively so that if we regenerated the dot o then we know that that's in the dependency list for the binary so we need to also regenerate the binary so this is that second main thing that build systems are there to do which is only rebuild files that it needs to because they the source had changed in some way right so I just want to be clear here if I were to this line it's using variables it's doing all of Lists right so it's it's using the features of make to do to make it look pretty that would be equivalent if I were to manually write it all out to GCC zero include directory that's I there and then okra this is actually the simple version I don't need any of less making life more difficult on myself so Oh for bin and X dot o y dot o so this right here is equivalent to lists but this that I have highlighted right now is using variables so I could actually change them at one point in this file and it will change the behavior at multiple points throughout this program right so for instance if I were to modify objects and add a third file so I let say I added another file into my program I add it there and there and then all of a sudden this command automatically works right I don't need to change anywhere else if I were to manually write all this out then all of a sudden I need to start changing everywhere where that's required right so you used by variables in programming languages and in general to reduce redundancy and make your programming life a lot easier right and that's what we're doing here ok so this is a simple make file it's nice because it provides a recipe for compiling any C file into a dot file it's nice because it does selective compilation of only those things that are required so if we were to run make here we don't need to do anything if we were to again let's modify this time X dot C so I'm going to modify it really stupidly by basically making a no op change so if I make that change then cool I know that I need to recompile X because I modified it and I know that I need to recompute the binary because I modified X right make is smart enough to only do the compilation jobs that it needs to and nothing more again in very very large code bases that can take tens of minutes to multiple hours to compile this is immensely useful right you don't want to wait for four hours to compile your program you just want to wait for the two seconds to compile only the things that you've changed right this is what build systems are there to do right okay so that's an example of a relatively simple build system however there still is a big problem with this so the big problem is that what if I take my Z dot H which I know is a header file and I want to change the constant I want to change the logic within it to be something else now I remember that Y has this constant that I just changed so I know that Y dot C should recompile now that I've changed the dot H file right I know that it has to because I've changed the logic in it right so if Y dot H should recompile because I changed e dot H wait a second it doesn't happen so here the problem is that make doesn't understand that there's a dependency essentially between Y dot C and Z dot H it doesn't understand that because it doesn't understand C it doesn't understand our programming language the dependency comes from our programming language and within our happy-happy make file that I can't access because Emacs is trying to be too smart there's no dependency here right there's no way that I'm saying to generate the dot o file for y dot o yes it depends on my Y dot C but also it depends on Z dot H there's no code here for that right if I were to add the code and explicitly modify it in this way then all of a sudden it will vomit because of a reason I don't want to go into next nevermind I'm gonna delay the discussion of lat long story short this make file is great and it will rebuild files when we modify them but only the dot C files it doesn't understand within my dot C files the dependencies because of the includes right so that's the core problem here and we need some way to solve that so I have a good build system I have a powerful build system add something let generically we'll build a lot of C programs but I don't have something let's maximally useful yet right so that takes us to the third example so we've gone through a simple build script a simple make file that allows us to do a whole bunch of things and now we're going to look at a feature full make file I want to show actually the text for that so I was writing lists and writing lists like half an hour ago and I realized as I named less to a feature full make file that every single time I read it I just read a fearful make file and it's painful how true that actually is but regardless it is a feature full make file so we will we'll stick with that right so this actually is exercising a lot of the really cool features of make that kind of take it from just being kind of a niche thing to being a full formed build system right and this example is going to show how we actually kind of take our make files and we can kind of integrate them with the surrounding shell and with the surrounding environment so I'm gonna go into my feature full build system and I've complicated things a little bit so I have my make file but I only have my X dot C in here and let's imagine that Y dot C is actually a kind of library for my project so now I have my library directory with y naught C in it and then I have my include file the Z dot H in a separate include directory so I've organized the structure of my program a little bit right so one I want to solve the problem where you know I really want make to understand these dependencies encoded in the include directives in my actual C program so that when I modify a dot H file it will recompile the C programs correctly right I really want that to work without that you can't really use make files with C properly right so let's look at this improved yet unfortunately a little complicated take on make first thing is that I want to I want you to look at least first three lines I've written this make file in such a way that given some assumptions these are essentially only three lines of code that you would need to change if you wanted to create your own program your own your own set of you know C files dot H files in all of lat and you wanted to be able to distribute the C files across multiple directories you wanted to have an include directory you could have as many C files as you want you can have whatever and these are the only three lines that you would need to really modify for it to be able to work so here what I'm specifying is where the directories that include our code are so it's this current directory and it's a library directory we know it's library because as I said within the note within the library directory I have my why not see in this version of the program right and then my include directory yeah look in the current directory in case I put a dot H file here and then also certainly look in the actual include directory right so these are the three lines of code where it's like you know you just need to say where your code is and the make file does everything else so this kind of is gonna show a lot of the power of make so I have my CC I have my variables to finding things within the system I added this new set of flags for dependencies these are the way that GCC and C compilers allow us to work with the compiler to allow make to work with the compiler well these are essentially telling GCC is when you build the programs don't just build the program so also generate dependencies in a way that make will understand so let if I modify one of the dot H files that this is dependent on it will also rebuild the C program so this is kind of a magical incantation that we use and pass to the C compiler to magically work with make okay so now my C flags include not just the normal stuff but also the dependencies okay now we're gonna get to the first weird a part of make files make files are turing-complete they have loops here we have a for loop so within a for loop here what I'm saying is create a variable D for each iteration of the for loop iterate through all of lis include directories the include directories are again all of the directories up here that I want to look for include files in and for each of loes DS with those directories within that list of include directories generate a - I and then the name of that include directory so all this is doing is essentially generating something that looks like I that and then I I did it include so you can see my include directories up here our first this directory and then include this for loop is just going through each of those and prepending - I onto each of them so this the output of this for each loop right here is essentially just going to be exactly less right so it's just adding the include directories into the see flags right [Music] somebody's asking if include is the name of the directory there is no include here so I don't I don't oh yes include is the name of the directory yes thank you so remember that I have this include directory here right so I know that my dot H file is in that include directory so yeah I better include it within that Inc der ok now we want to find our C files in the last make file we kind of manually listed out all of our C files right that's fine and there's some programs some some systems that do that xv6 absolutely does lists right so some pro some C programs will do that however we're trying to automate lists we're trying to make this a nice environment to use so what we're gonna do is we're again going to use our iteration this time we're going to iterate through all of the directories within our code ders right so that's going to include our library directory and the current directory so we're essentially saying that the current directory includes my X dot C it might include other C files I don't know and my Lib directory also includes dot C files so I want to go through all of those and treat every C file within that as a C file that I can track within this variable so I'm iterating through all of those code directories the current directory and Lib for each of loes I'm kind of setting a variable equal to D for that iteration and I'm using this wild-card thing wildcard essentially just says yes so within that directory it might be this current directory dot where it might be Lib find all of the clean star dot C things right so find all of the files that essentially end with dot C we know that those are C files and it'll list all of those out right so all that this is doing is essentially creating a list if we were to expand this out C files would essentially be equal to let's see do to do B X dot C and Lib y dot C so let's see that so I know X dot C is in the current directory and within Lib directory I have Y dot C so this is just programmatic code or essentially generating this list so it's just every every dot C file that I have right cool so now I have a programmatic way for finding all of the dot C directories all that I needed to say was essentially where what directory is to look in right and this is really powerful because now when I'm developing my program when I'm developing my code I can add in these dot C directories will I'm sorry my C files I can add in new ones remove them and I never need to modify my make file it just automatically finds all the C files for me builds them properly and I don't need to worry about it a good build system after it's properly created just kind of gets out of the way right is there a way to programmatically recurse into sub directories assuming we have sub directories and include or Lib there is I'm not going to go into it because we're getting into even more complicated things one thing that I should live list related to lat but a little bit more roundabout is that within a make file within a rule so I have my all again I could actually call make within a subdirectory so if I had a make file to find in the Lib directory this would actually recurse into low library directory and call make lair so there are ways to kind of recurse and you know do a whole bunch of stuff in a complicated program in a complicated project and they are used in a lot of complicated projects I'm not going to go as much into them right now but a lot of them include that kind of just calling work make files within the sub directories Oh the next kind of tool that we have in our makefile belt is pattern substitution within strings so I've generated my list of C files now and now what I'd like to do is generate my objects write my objects are the dot o files so all that this does I have my pattern substitution tool within make and all that let's doing is for all of within the C files C files is a list of all my C files so for each of those items within the list take anything that matches something dot C and of course they're all C files I only looked for things that match something dot C right so that's going to match all of them convert that into that something dot o so that's going to convert all of my C files into the dot O's so I'm going to now have X dot o and Lib Y dot o so this is just replacing all of those previous dot sees that were right here with datos so it's programmatically generating all of my dos those are the datos that are going to be created by the build system and then the way that make works with the with GCC with the C compiler to generate the dependencies is so again let me let me rewrite within my C programs I have Lee's includes write that that says that there's a dependency on the dot H if the dot H changes I better recompile this file right the way let GCC does that is certainly as I said you pass in these flags and I'll kind of do it magically for you but the way that it does it is it generates dot d files so this is all of those dot d files instead of replacing and finding all the dot o--'s of object files I'm just creating dot d files it's kind of unimportant what's happening I'm not gonna explain any dependencies in great detail if this is I'm giving you a pattern to follow that will work this code is exactly the same as we had it before this is a default rule that is executed it relies on the binary the binary relies on all all bla objects I've now programmatically generated all those objects which is pretty awesome and I have kind of that same rule as I had before for essentially saying yeah compile each of or compiler binary from all of la objects so that's cool and because I have this dependency on all the dot o files the dot o files are gonna come here and say well I know how to generate dot o files it depends on the dot C files when I depend on the dot C files I have kind of this same rule for essentially generating the compiler directive for compiling one right so I actually kind of have really the same thing as I had before where if I build it just kind of generates the same commands the only difference here is that I am generating the da file in the library directory because I've moved my Y dot C into low library directory that's fine and I've kind of now including this include directory in my list of the include directories where when I do a pound include it will look in that directory to see if the dot H file is there right so everything's kind of the same here right it's kind of generated the dot o is just the same it's also generated a dot d file here again that dot d etat d file somehow works with the make file to kind of say hey make when this dot c file is looked at you better look at all the dot H files that are included in somehow that X dot d includes that information about which dot H files the X dot C depends on right so again if I were to remove the binary cool it just needs to run the last command to generated a binary from dot o files if I were to modify the X dot C function or sorry C file then it has to rebuild the X dot C cool great but now because I've included all of this information I've included for the dependencies I basically passed these magical flags to make that magically generates the dot d files now make if I were to touch the where as it as include Z dot H include Z dot H and I were to change this to 43 now I've changed the not see it the Z dot H let's include it in both Y dot C and in Z X dot C so I know that they both better recompile now it actually works correctly and they do both recompile now that I've compiled everything it doesn't need to do anything again if I were to yeah touch anything it's only gonna recompile the corresponding things that need to be recompiled right so this is what make does right and this is what complicated make files can actually do there's one last thing that I want to go over before we kind of terminate this is the example of how we actually use the dependencies I I'm not again not to go into how make interacts with that or what the dot d files do this is just kind of the magical thing that you have to do to make it work you can read more online but I want to also know that make files are really cool because they're basically kind of the magic of shell scripts combined with all this logic about what to build and all these variables and all these cool things right because if I do a make diff here I've added a rule to essentially get and to call get to get some cool information about changes that I've actually made within my program in the meantime right so let's look at what that says well git status tells me what files have been modified and more importantly more usefully what files are untracked if I forget to you know add a dot C file into my to add it into get then this will remind me right so I really want to do this always before I do push and I just want to often say well ok our live files have been modified the ones that I think are modified so this is just a sanity check for me I like running get stat to get this information but Len I'm also running get DIF stat which gives me a summary of which files have been modified and how they've been modified so readme MD I've added 37 lines to it future or z.h I've added two lines I've added a line and removed a line right so I've changed one line essentially so this is just a useful thing this is equivalent to saying git diff stat gives me the same information right but now I've created a make file command that will essentially do it all for me further I didn't want it to print out the fact that I was calling git status so oops when I call make diff I don't see the lines where it basically would have said get diff stat I don't see that line here so in make files you can actually use this @ symbol before a line to essentially say do not output this line until a console run the command but don't print out the command you can also see that at the top of this I have kind of added this note the status of the repository and the volume of per file changes so this is just something that I wanted to output at the top of kind of list if stuff so you have the info command that you can use to essentially output something when this rule is actually executed right so you could do really powerful things with this you can write rules that help you automate many of the common tasks that you might want to run within your repository so one of these another one of these that I've added is get distribute if you want to distribute your code in a binary format to people then oops it's not get its make duh so what I've done here is I just have made a make rule for actually creating a tarball for my entire repository so now you can see there's is this disk GZ that's being created that includes all of my code right so that's great and you can see that that was just a simple rule here create lura the distribute rule definitely clean the directory before distribute runs I don't want to distribute all the dot o files and the binary and then just run the tar command to zip up and put into the disk you see file all of my files right so point being you know make is a powerful thing and it can really automate not just the building of code but also some of the common tasks that you perform within your repository and the notion of dependencies is a generally useful thing we've seen how dependencies are absolutely necessary so that we only recompile what needs to be recompiled ie only when I change the dot C file do I actually need to recompile this but we've also seen here with distribute that I will want to run my tar file my tar command here but before I run the tar file I want to make sure that I've removed all of the intermediate dot oh and binary files so I just add a dependency layer I don't need to run the RM again to do all of that right so I don't need to type all of this out again redundancy no I just say yeah run clean before you run distribute right [Music] yeah so anyway that's all that I really have for lists that's all that I have about make files I did not I should actually make it so let I remove the distribution stuff when I run make clean as well but I have not so that may make diff what changes have I made those all seem like good changes now I will commit on this today ten to nineteen post make modifications cool I looked at what I've changed before I actually did my commit and now I'm ready to do my push right so that's essentially all that I had to go over for today but before I stop there was a question generally about something else before I got into make so certainly if you have any questions about make for those of you or left feel free to ask but otherwise that's all I have okay yeah I'll go into Lua the infinite loop thing okay so I'll let you queue up any questions that you may have about lists and then I'll just quickly answer one of the other questions that somebody had so there's a question more generally about what the heck this is that's just annoying see syntax for essentially that is just an infinite loop that's all do I have any make resources to suggest let me go back to read me I actually don't honestly make is a little too old and I've never found good resources the best resources that I found on make are just other build systems other make files and honestly most of the useful stuff in May I've gone over to be honest I I don't think that the other stuff is kind of a higher order bit once you get adept at using some of the stuff that I pointed out here then reading through the the make manuals you'll find what you need you kind of look through the commands and there it is right but I don't know of any good resources I apologize so make is a very old build system certainly newer build systems exist make is exceedingly powerful it's the most powerful build system that I know of but usually with power comes a lot of options options mean that you have to write a lot of code within the build system to do things right so a lot of more modern build systems make more assumptions about what the build system needs to do and don't let you quite do as much so it makes it easier to read and modify the build system because they're often less powerful however there are a lot of different build systems that have many different constraints so Auto Comp is another old build system that actually is a build system for build systems it generates make files based on your system configuration so if you're writing software in C or C++ and you rely on some libraries Auto Kampf is a great way to basically generate code corresponding to what libraries are present on the system or not antonie maven or popular build systems for Java they have a lot of the same primitives that we've gone over here with make wildcard completion dependencies targets all of that stuff ninja is interesting because it is a build system that's both as powerful as make and kind of horrible to use in the same way as make like alerts a lot of verbose stuff let's there and that's because ninja is supposed to be an output from a build system so it's supposed to be a very it's it focuses on being fast 100 percent make is actually not that fast because you're running a whole bunch of shell commands when you're doing a whole bunch of these rules ninjas meant to be very fast but it's not as feature-rich so but you have build systems that generate ninja build systems see make is a cross-platform build system that again generates other build systems so see make is an example of a build system that actually generates make files and ninja scripts if you want lat for you but you do need to configure it and it's somewhat esoteric thing to do and you can't do as much as you could do and make and there are many many more I include a list that you can find within this repository so let's pretty much all that I have for the night unless there are any other questions so thank you all very much hopefully this gives you context build systems are one of those things that you never really want to learn and you never really have a perfect time to learn it but they are really important to learn so hopefully this gives you a lot of pointers and hopefully you can actually understand most of lacks v6 build system at this point from what we talked about it doesn't do much more than what I went over now so good luck enjoy the night and I'll see you all in class tomorrow bye bye
Info
Channel: Gabriel Parmer
Views: 114,731
Rating: undefined out of 5
Keywords: makefile, tutorial
Id: DtGrdB8wQ_8
Channel Id: undefined
Length: 61min 53sec (3713 seconds)
Published: Sat Nov 02 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.