Master GNU Makefiles: Streamline Coding Builds with C++, C & Assembly

Master GNU Makefiles: Streamline Coding Builds with C++, C & Assembly

Tired of typing endless compile commands? Join me in this fun, hands-on guide to mastering GNU Make! We’ll walk through creating a Makefile to simplify your coding builds, covering C++, C, and assembly projects. Learn how to set up variables, define functions, manage dependencies, and organize build directories to cut compile times drastically. From practical tips like using clean targets to advanced tricks like dynamic variable assignments, this video has it all. Whether you’re a beginner or a seasoned coder, you’ll see how Makefiles can transform your workflow. Plus, I share a real story of turning a 1-hour build into 1 minute! Subscribe for more coding tutorials, and let me know how Makefiles change your game. Scan the QR code for more resources!

Introduction to Make Files 00:00:00
What is a Build System 00:00:11
Problems with Manual Compilation 00:00:16
Using Scripts vs Build Systems 00:00:36
Benefits of Make Build System 00:00:56
Personal Experience with Build Times 00:01:15
Creating Sample Source Files 00:02:20
Installing Make Build System 00:04:56
Creating a Makefile 00:06:02
Defining Functions in Makefile 00:06:46
Calling Functions and Variables 00:07:13
Setting Up Compiler Variables 00:10:51
Dynamic vs Static Variable Assignment 00:11:56
Compiler and Linker Flags 00:12:44
Setting Up Build Paths 00:19:16
Printing Variables for Debugging 00:21:24
Understanding Targets in Make 00:25:10
Dependency Graph and Compilation 01:02:04
Managing Build Directory 01:03:11
Creating Binary Target 01:05:44
Handling Object Files 01:11:29
Running the Program 01:13:16
Clean Target for Cleanup 01:17:00
Advanced Build System Features 01:22:46
Git Ignore for Build Files 01:24:34
Conclusion and Call to Action 01:25:08

Thanks for watching!

Find us on other social media here:

  • https://www.NeuralLantern.com/social

Please help support us!

  • Subscribing + Sharing on Social Media
  • Leaving a comment or suggestion
  • Subscribing to our Blog
  • Watching the main “pinned” video of this channel for offers and extras

Hey there, let’s talk about using make files and the make build system.

So what’s a build system? It’s basically just a program that helps you compile your program.

In a previous video, I talked about the idea that it’s really cumbersome

to type out build commands, you know, compiling commands, linking commands and so forth for your

cumbersome and you’ll forget everything that you’re supposed to be doing. So the next upgrade

from that was in a previous video I talked about let’s take all the commands and put them into a

script so that we don’t really have to remember all the commands. We can just, you know, create

a build script and then just kind of run it when we want a program to compile and link and execute.

But then the thing is about a script is if you have a very, very large program,

it might take a very long time to compile. So a build system will take care of that for you.

care of that for you the build system that i’m about to show you the make build system

it can carefully calculate which source code files you’ve actually changed since the last

time that you built your program and it will only rebuild the source code files

that actually need to be rebuilt you know into their corresponding object files and such

so for as a personal example long time ago before i learned how to use build systems i had a giant

And every single little change that I made, I would just have to wait for like an hour.

Like I’d have to go get a sandwich or go do something else or work on a different project for an hour.

Then come back and see if my changes actually affected anything.

Somewhat quickly, I got irritated enough that I looked into build systems.

And I realized that make, the one that I’m showing you today, is pretty easy.

It goes really, really far, but it’s pretty easy to get started.

And it drastically reduced my compile times.

set up, you know, the make file and everything so that I let the build system do the building.

My build only took like a minute. So I could do like a quick change and then just wait one minute

to see the results and then another quick change and so forth. Okay. So if you look at the screen

here, I’m sitting here on a blank desktop. Hopefully I’m recording everything. Am I?

I’m sitting on a blank page here. So let me just make a couple of source code files. This is not

This is not a tutorial for C++ or assembly or anything.

I’m just going to copy paste a couple of sample programs in here real fast.

So I’m creating a C++ source code file.

I’m calling it first.cpp and it’s going to call functions from other source code files.

This is going to be a hybrid program.

Again, don’t really worry about this.

My other videos already explained hybrid programs and so forth.

I’m just going to show you how to use the make build system.

the make build system.

So let’s make another source code file called second dot C.

And then another one called third dot ASM.

So I’m linking a I’m linking three source.

Whoops, that’s wrong.

I’m linking three source code files.

And they’re all in different languages just to prove to you that this will work

with a hybrid program.

And so you can kind of see I’ve got first, second and third.

The first one is C++.

The second one is just C.

Third one is assembly.

If I look at it real fast, get ready to pause the video.

real fast get ready to pause the video then you can see that it’s C++ and if we look at the second

one it’s just C and if we look at the third one it’s just a very simple assembly program and they

just sort of say hello and that’s it okay so you know at this point in my other video I would have

made a compile script I’d say nano compile and then I’d stick a bunch of compile commands inside

of it let’s see do I even have that from last time let me see if I got it if I have it I’ll

I got it if I have it I’ll just paste it real fast just to show you compile I don’t think I’ve

got it although I do okay I’m just gonna paste it in here and it’s not the point of this video check

my other video if you want to kind of understand everything that’s going on in here but it’s just

a script so I’m gonna go compile clear and then list and then in the last video I just said let’s

and it compiles everything and it links the binary and then I can run main which is my binary and then

it just runs this program so it’s no problem it seems okay but again imagine that your program is

huge many many lines of source code in every single file and many many many source code files

and perhaps you’re pulling in libraries and shared objects and just like you know a bunch of other

stuff your your your building could slow down drastically so I’m going to remove the compiler

the compiler script and then I’m going to manually remove all the compiled

object files. Got to be careful with this. Remove the binary. Okay. So now I’ve got

that one, two, three. Okay. Now I’m ready to set up a make file. So I guess first

you probably should have the make build system installed. If you’re if you’re

learning about C++ and assembly you probably already do but just just so you

know it’s a it’s gonna show up in Ubuntu as the build essentials package so

So sudo apt install build essentials.

I can’t remember if this is plural or not.

It’s either build essentials or build essential.

You can do an apt search or a DNF search depending on your system.

We can do build essential and see what shows up.

So I’m just going to go to the top build essential.

Okay, so it was singular, not plural.

I have it installed already.

Let’s see.

If you just want to install make by itself, you can probably do apt search.

search, just make and go up to the Ms.

You can see right here.

It’s just pseudo apt install.

Make is all you really have to do to get the whole make build system.

They’ve got a website, you know, search for Gnu make.

I’m going to type it on the screen here.

Gnu make search for that.

You’ll get a nice website with a bunch

of documentation and examples that go way beyond this video.

But I’m just going to show you the basics so you can get started.

So the first thing you should do is create a file called make file.

create a file called make file. I think under lowercase m is supported but I usually do uppercase

m which is obviously supported because I always do it and what you’re really going to do is you’re

just going to create an empty file call it make file and then inside of the make file you’re just

going to give make a bunch of commands and things that you want it to do and then later when you’re

actually ready to compile you’ll just type make as a command and then give make an instruction and

have lots of instructions and targets available inside of the make file for

make to grab onto. Okay, so moving on. The first thing I want to show you is how to

define a function in make. You probably won’t do this a lot, but for me I kind of

like to print, you know, pretty messages and sort of like do repetitive tasks in a

function. So when you want to define a function you just use the keyword define

and then you name the function. So I’m gonna make a function called say which I

see what it’s for I know it’s silly define say and then I’m going to tab here and then I’m going to

tell it to print some information I’ll just put test info for now the thing about calling functions

is you usually put a dollar sign and then an open parenthesis and then the name of the function and

then after that arguments to the function so here you can see actually let me get line numbers up

here on nano you can see online too I am calling a function called info it’s a built-in system

it’s a built-in system function you don’t have to define it yourself and I’m

telling it I want you to print some info to the screen and I want you to print

test info to the screen and that’s it so in my make file I can just call that

right away with call you know dollar sign parentheses and then the keyword

call and then name the function that I want to call closing parentheses and

then if the function takes arguments I put a comma and then just start

providing the argument so I’ll just say hello what’s up actually this shouldn’t

up. Actually, this shouldn’t take my arguments right now. We’ll upgrade it in a second. But

so this is a very simple makefile. It doesn’t actually have any targets. It will just simply

do things. This kind of stuff is useful. At the very top of your makefile later, we’re going to

define like a bunch of variables. And sometimes it’s nice to print them out. So I just want to

show you how to start printing so that maybe if your makefile isn’t working, you’ll realize that,

oh, that variable is wrong because you printed everything when it started up. You don’t have to

You don’t have to do it that way.

I just like to.

So how do you invoke the make file?

We just literally type make.

And because we named the make file, make file,

the make program will automatically figure out

where it is and go find it and load it.

But you can specify the name of a make file

if you really want to.

I think it’s the dash F switch.

Actually, let me look it up just to be sure.

I’m gonna go man make and just go down a second here.

Yeah.

So this switch right here,

here dash f and then specify the file name or file path or dash dash file and then specify the name

or path that’s how you could use a non-standard make file but almost no one does that everyone

just likes to to name it this way and then just type make so if i type make notice how the first

thing it does is it says test info which is what i told it to do in that function which i called

and then it says no targets gotta stop because we didn’t add any targets yet okay by the way if

about what I’m even doing this for like compiling and whatever go see my other

videos where we talk about compiling and linking and all that stuff this is just

to make things a lot more easy and powerful so let’s upgrade this save

function and I’m just gonna put some brackets and I’m gonna say my super cool

make how about like builder I guess and then instead of test info I’m gonna put

dollar sign one which basically is gonna tell this function you know when I call

say, then the dollar sign one variable is going to show up as the first argument that I provided.

So now when I call say, it should have hello, what’s up as the first argument that goes into

the function. So that means it should print my super cool builder and then the message that I

had it print. One more time, notice how it says my super cool builder. Hello, what’s up, I just like

to do it this way, because then it’s easier for me to differentiate messages that the build system

itself from the actual make file and messages that maybe the tools I’m calling on are giving like GCC or whatever

So let’s edit this a little bit more

The second thing I would like to do is well, maybe I’ll change this to just you know now running or something

Now let’s set up some variables. I’m gonna do like a little thing here. I’m gonna say variables

Put a bunch of comments in make files comments are just hashes

So, you know, this, these lines right here don’t really do anything, 9, 10, 11.

And I’m going to start setting up some variables for my compilers.

So again, actually compiling is in a different video.

We assume right now that you already know how to do it.

So I’m going to say, let’s do some variables for compilers.

You don’t have to do it this way, but the standard convention for naming a regular C

compilers or GCC seems to be just CC as the variable name.

So I’m saying a variable named CC is going to be equal to GCC.

to be equal to GCC and then from this point forward every time I use the variable CC in my

script in my makefile it’s actually going to be like typing GCC and you know if you don’t understand

why you would use variables you probably need to learn how to code a little bit more but basically

it makes everything more powerful instead of typing GCC everywhere and then maybe later if I

want to change it I have to go change it in a million places now I can just change it in one

place and so it’s more powerful let’s do the same thing for the C++ compiler I’m going to say G++

compiler I’m gonna say G++ same thing for my Yasm assembler I’m gonna say

we’re using Yasm also notice how when I assign variables I’m not just using an

equality or an assignment operator I’m not just doing equals that is a little

bit different of a way to assign you can do that but if you just put an

assignment operator by itself just like the equal then if you later change

depends on on the right side, like another variable, then it will update on the left

side. So it’s like, it’s not really a permanent assignment. It’s more like a dynamic assignment.

So, you know, if you want to make sure that the variable has a fixed value that doesn’t change,

even if you start changing variables that it depends on, then use colon equal. I’ll just leave it at that for now.

Not super important. You can also tab and, you know, kind of line things up and it’s no problem.

just to make things look nicer.

So now let’s set up some flags for C and C++ and YASM.

So I’m going to create a variable called ccflags,

do the same thing again where I do the colon assignment operator.

I’m just going to give it some flags.

I want it to show me all warnings,

and I want it to be pedantic about its warnings,

and I want it to generate debug symbols.

build system we want to try to compile source code files into object files first

and then at a different stage link the object files together we we don’t want

to just compile everything in one fell swoop directly to a binary in one line

because we wouldn’t really be leveraging the power of the build system in a

moment I’m going to draw a diagram that tries to make this a little more clear

for now I’m just typing up variables so just bear with me when I’m you know

Cpp flags.

So for C++, I’m actually going to reuse a variable here.

I’m going to reuse the CC flags because all the flags

above are fine for C++ as well as C.

So that means like in my C++ compilation,

I want all warnings and I want it to be pedantic and I want to generate debug

symbols, but in C++, we’ll specify the C++ standard.

So C++ 23.

kind of already leveraging variables against other variables to save typing and make things a little

bit more module. If I decided that all my C was not going to show warnings or was going to convert

all warnings into errors or, you know, use a different flag or something, all I’d have to do

is change it in one spot, line 20 and line 21 would automatically get updated. Also keep in mind

because like I said before, we have this colon assignment operator here going on the right side.

value of cc flags then it would be reflected if i didn’t use um the assignment operator you know

what let’s let’s just do it let’s just do it real fast i’m going to say call say i’m going to say

let’s let’s see cpp flags is and then i’m just going to print cpp flags you print variables kind

of the same way that you call functions you just put a dollar sign and then surround the variable

name in a set of parentheses and that’s pretty much it so i’m going to copy this line a couple

And in between CPP flags, actually let’s do CC flags.

We’ll assign CC flags to something.

I’ll say hello test.

And right now the way CPP flags is written, the assignment kind of sticks.

It’s like permanent fixed.

It doesn’t dynamically depend on CC flags.

So we should see two instances of the exact same message.

Let me open up another window here because I want to keep that editor open.

because I don’t I want to keep that editor open so we’ll do a make and notice how when it prints

the CPP flags they’re the same it’s the same both times even though I changed CC flags in between

those two messages on the other hand if I don’t put that to colon if I just put a sign then now

it is dynamic notice how the second message uses the modified version of cc flags so keep that in

So keep that in mind if you want a variable to be able to change anytime you depend on

anytime a dependent variable that it depends on changes you’ll assign one way and if you

don’t want it to change you’ll assign it another way okay so that’s enough of that I’m going to

take away that message and I’m going to change this back to non-dynamic okay then we’ll do the

same thing for assembly assembly flags this is going to be the Yasm compiler I’m going to tell

I’m going to tell it that I want ELF 64.

I want a 64 bit executable.

I want to dwarf to debug symbols.

I think in Yasm you have to specify the type of symbol.

I think I can’t remember if you actually need to.

So you could try just, you know, dash G if you want.

It may or may not work.

But I always just like to specify at least for assembly.

So then let’s do some linker flags.

Okay, so link flags, you know, what is the linking stage?

What’s going to happen in a hybrid program?

You know, for me, it’s just going to be the G plus plus GCC linker.

So that means I’m going to do all the CPP flags because I want wall and pedantic and

debug symbols and I want the C plus plus standard set and all that stuff.

Whatever you want to do is fine.

And then I want to just specify.

I don’t think you have to do this on modern systems, but I’m going to specify I want to link a 64-bit executable

And then I have to specify no pi

Which I think is kind of an Ubuntu thing your program probably won’t run if you don’t do this

So just trust me for now put no pi in the linking stage and then we’ll do another flag called no exec stack

Which is also a somewhat new thing which prevents

stack which means you might be less hackable not sure if it’s how safe it really is but you know

probably improves the safety a little bit uh let’s see at the last minute okay so now uh

now that i’ve set up the link flags uh i need to like add on the dash c flag for the actual

compiler flags because uh you know if if you know this from my previous videos if you’re going to

compile to an object file and not directly link everything right away you need to add the dash c

away you need to add the dash c flag to let the compiler know oh i just want to compile to an

object file and then later i’ll come along and link so i’m going to go cpp flags is equal to

and i’m going to say equal to itself and then just add a dash c at the end of it and then we’ll do

cc flags and then it’s going to equal to itself with just a dash c at the end of it so

I had CPP flags on line 21 equal to just this basic thing.

And then I reused CPP flags for the link flags,

which means if the link flags didn’t use that colon,

I would basically have ruined the link flags variable by the time I went down

here and changed it. So you don’t have to organize your make file this way.

You can actually hard code a lot more stuff into the variables.

I just like to try to be more dynamic. It’s more fun.

up all the basic variables for the compiler let’s set up some variables for paths so i’m

going to set up paths here so what do i mean by paths you know this is a build system it’s got to

know where things are right so for starters you probably want to name your binary you know the

executable file so i’m just going to make mine main you can make anything you want like my program or

whatever but you know for mine it’s going to be main and then i’m going to set up another variable

going to set up another variable that depends on it so I’m going to say the actual binary path is

just going to be you know this main meaning the current directory and main also you know most it

seems like most make files have it set up so that all of the intermediate files like the the object

files and the binary they get built in the current source directory you know like the root of the

Sometimes also I like to just make a build directory.

So it’s really, really easy to just kind of clean everything out.

You just delete the build directory and you don’t have to worry about the repository getting

super cluttered with object files in the meantime.

So I’m going to name a build directory here.

I’m going to say build directory name is build.

And then so you can imagine there’s going to be a folder here now named build that shows

up whenever, well, I’m going to add more targets later when I actually start building.

when I actually start building, we’re still just naming things.

So then I’m going to name the actual build directory as the current directory slash and then build dir name.

Okay.

So that means the bin shouldn’t just be in the current directory.

It should be in the build dir and then a slash.

And then instead of typing main, actually, I should probably reuse that variable.

So let’s do bin name.

And again, reusing variables is really, really good because it just saves you work.

I can change things in less places when I leverage variables more.

If I had the word main written down all over the program, I’d have to change that in every

single spot of the make file if I wanted to change main to something else.

But in this case, I only have to change it in one spot.

Okay, so now maybe it’s probably a good idea at this point to start printing out what we’ve

done.

So I’m going to do a bunch of, you know, calls to print things.

you know calls to print things print everything out just for double checking ourselves

okay how can i do that i’m going to do that with a call uh the uh say you could also just do uh info

and then you know something like that like we did up above in the save function if you want a less

fancier build system but i like doing it this way so i’m going to say what um let’s do cc uh

CC and I’ll just put maybe like a quality operator or something and then

I’ll just print out what I put for CC and I’ll just start duplicating this

line a few times and I’ll say I’ll just print out everything that I added as a

variable CPP and then ASM type ASM and this just kind of you know it just kind

typed something in incorrectly might take me a while to figure that out but if

I print it all out at the beginning of the of the make file running that I can

just visually inspect what the variables actually contain so let’s do CPP flags

and then we’ll do CC flags

SM flags.

And then we’ll do one more.

It’s going to be probably, oh, link flags.

There we go.

So link flags.

Bringing out a lot of stuff here.

Then I can print the directories, I guess.

So maybe I’ll duplicate this a few more times.

I want to do builder.

The build directory, just so I know where it is.

and I can verify that it’s like a real path.

Builder, maybe I’ll duplicate this one more time

so that I can see the absolute path

to the build directory.

Just for my own information,

because sometimes if you get it wrong,

this is another way to kind of see what’s going on.

So there’s a function in make that’s built in.

You pretty much just say dollar

and the name of the function is abspath.

path and whatever you put inside of it it’ll try to give you the absolute path of that thing

so i’m just telling it now that i want to print the absolute path of the build directory

by the way um you probably don’t want to convert all of your variables to absolute paths because

make has a huge problem with spaces in paths and file names so keep it relative unless you’re just

going to print something um you’ll find out the hard way i mean feel free to try it and prove to

yourself it’s not a good idea um but i found it the hard way many times because i kept forgetting

I kept forgetting. So then we’ll just do the binary, I guess. Okay. Yeah. We’ll do one more

for the binary. It’s going to go there and maybe I’ll duplicate this so that I can say

what the absolute path is. Okay. Abs path. And then if I screw this up, I’ll just look up the

I’m going to do clear and make so I can just see what it’s going to print.

And let me just double check here.

Okay.

So everything looks good there.

Looks good there.

Looks pretty good.

Absolute path to the builder is my stuff.

All right.

Okay.

So seems good so far.

I’m just going to leave that in there because it’ll be easy to double check it.

And now we’re going to do targets.

So what is the target in the make build system?

in the make build system a target is just something that you might want to create or or

or build or compile or something targets are not only for uh building uh executables or object

files or you know something that you make targets sometimes can just be called they’re called phony

targets but you can just call a target and it will execute a bunch of commands within that target

even though it’s not building something specific that is needed so you can use make files to do a

really powerful stuff. Let me show you first off, we’ll do a we’ll do a target. I’ve already

prepared something up here. I’m just gonna I’m just gonna copy paste it here because it’s going

to take a long time to type if I don’t. So here’s a target. An example of a target is okay, I’m going

to name the target the target menu and I’m gonna put a colon. So now later I can type make menu

and what will happen is it’ll execute all those commands that are listed below. Each command

is really just going to be a shell command.

You have to use tab to indent for the command itself.

Notice how I have a bunch of echo statements

and then I’m just like printing stuff, right?

So this whole target, it doesn’t actually build anything,

but it will print a menu to the screen for me,

which is pretty convenient.

When you’re not actually building something,

like you’re not building the executable

that the target doesn’t name an executable,

then it’s called a phony target.

And you want to name it as a phony target

phony target with this line 67 down here because if you don’t the build system might actually

eventually end up getting confused between a fake target and something that might be sitting on your

file system like if i accidentally put a file named menu on my file system at the root of my

repository then make might get confused and think well i see the file so therefore i don’t need to

build it and then the menu will never show up again so this way i’m saying this is a phony target

at the file system it’ll just always execute that target for me so this is the basics of a target

you just name a target put a colon and then every line under that is a shell command that you wanted

to execute so let me just show you that real fast we save to be sure so we’re going to do clear and

actually the absolute paths are starting to bother me let me get rid of them right now

head before I made this video. Okay, and we can just sort of trim the fat a little bit. So I’m

going to do clear and make. And notice how the menu target just ran by itself already.

The convenient thing about make files is the first target that shows up is going to be known

as the default target and it will just get executed automatically if you don’t specify

what to execute. So notice how I just typed make by itself and I didn’t actually specify a target.

I could specify make menu and the same exact thing happens.

Notice how I said make menu, the menu shows up again.

But if you don’t specify anything,

then it’ll always execute the first found target.

And just to prove that to you right here,

I’ll do maybe I’ll do like a different target

and I’ll just call it hello.

And I’ll just have it echo hello.

So now if we just execute make with no arguments,

notice how it just prints hello.

if I want the menu again, I got to go make menu.

Right? Okay. So I’m going to, I’m going to set the menu back to the default target.

Another thing that sometimes people do is they’ll just make a target called default.

I guess I’ll do that here, make a target called default. And they’ll say that the

default target depends on whatever they want to actually run. And you don’t even need to

call it default. You can call it hello or anything that you want. So, you know,

put to lead default just so you know that this is not a standard system name that’s expected so

the default target it’s just the name of the target then a colon and instead of putting shell

commands underneath i could put dependencies on the right so now i’m saying that the default target

depends on the menu target which means in the make build system first what it’s going to do is it’s

going to go check to make sure that main or sorry menu is satisfied because menu is actually a fake

Because menu is actually a fake, you know, a phony target, it’s always going to get executed.

And then when it’s finished and laDefault realizes, okay, all of my dependencies are satisfied now,

then laDefault is going to execute and it’s actually going to end up doing nothing.

I mean, we could put some commands under here if we want.

I’ll put the word nothing.

And just to prove to you that menu will get executed first because it’s a dependency of laDefault.

And then finally laDefault will execute its commands and print the word nothing.

execute its commands and print the word nothing.

Let’s do that and then I’ll erase the echo command there.

So we’ll do clear and make.

Notice how the menu got printed first

because it was a dependency of the default

and the default is the default target.

And then after the dependencies were satisfied,

then the default did its thing and printed nothing.

But so let’s get rid of that.

And now it’s just like when you do make with no arguments,

then the menu gets printed.

printed. Okay, so what else can I talk about real fast here? Let’s talk about

silent commands. I’ll put a little comment menu. Let’s talk about silent

commands. You see all these at symbols here? There are a bunch of symbols that

you can use throughout a make file. I’m going to show you just this and a couple

other ones, but basically this means don’t print the command you’re about to

execute, just execute it. So let me show you an example real fast of what I’m

what I’m talking about.

Targets to show silence and non-silent commands,

or a suppressed command, whatever you wanna call it.

Basically not printing the command,

just executing the command.

So I’m gonna name a target silent commands,

and it’s not gonna depend on anything.

So that’s why I’m gonna hit enter here,

just like with menu and unlike the default.

So then I wanna do tab to enter a command

to enter a command and the echo command is going to say hello this is a silent command or a

suppressed command then i’m going to do basically the same thing again but i’m not going to put the

at symbol in front i’m going to say hello this is a non-silent command so what we should see is that

if we exit the silent commands target we should see first this message gets printed on line 75

and then second we see the full command including the echo statement that shows up on line 76 and

on line 76 and then third we should just see the message hello this is a non-silent command

let me oh and then if it’s a phony target meaning it’s not an actual file on the file system

that you expect to be built we have to do phony and phony is actually a target notice how i’m

saying dot phony colon indicating that that it’s a target and we’ll just say that it depends on

and we’ll just name the command that is also a phony or the target that’s also a phony

you can specify all of the phony targets in one line if you really want to but for me that kind

of feels messy and you’re allowed to like add add add dependencies so notice on line 70 i’m adding

menu as a phony target and then on line 77 i’m adding silent commands as a phony target

the way you do it is up to you but i like to do it this way so i’m going to do silent commands

and then I’m going to do make silent commands.

So now we’re actually going to execute the silent commands target

so we wouldn’t see a menu.

And then we’ll see an example of the silent commands.

Notice how the first one that says,

hello, this is a silent command.

The actual echo command is nowhere to be found

because that whole line was muted

and only the output of executing the command gets shown to you.

But for the second one,

remember we did not put the at symbol on the second command.

So the whole entire command gets printed.

the whole entire command gets printed echo and then quote this is a non-silent command and then

that command gets executed and here’s the result it’s the same string except just without the echo

so you know this line right here is this is the command that we’re about to execute whoops

this is the command we’re about to execute and then the last line is this is the result of

executing that command why would you want to print your commands kind of useful sometimes to see the

compiler statements that show up there have been a few times or more than a few times in my life

times in my life where I was running a make file and just kind of watching it and it was printing

out all the commands that it was executing and then I realized oh my god that’s wrong because

it was printing and I was able to fix that so you know you don’t have to print your commands but

you probably want to so let’s see what else can we do okay so here’s another thing that I want to

show you how to ignore failures so sometimes if you’re compiling your source code won’t compile

So then that means the compilation command failed.

Or if you want to do any command in general,

and for some reason it returns false or it fails,

then that’s considered a failed command.

The make build system will stop execution of any target

as soon as it encounters a failure.

It’s kind of a good thing.

What if you had a huge complicated target,

which you can do it this way.

I’ve done it this way before.

What if you had a huge complicated target,

which not only builds everything,

but then also tries to deploy it to your remote server?

deploy it to your remote server.

Well, if your program fails to build, you probably don’t want the deployment part to

execute.

So you would hope that it would stop as soon as something failed to compile.

Right.

So we should try to know the difference between failed commands stopping the make build system

and failed commands being ignored by the build system and just continuing.

So we can do it both ways.

So I’m just going to do a little comment here.

or ignoring failures and then i’m going to do ignore failures so that’s a target no dependencies

and i’m just going to do false false twice in bash if you just you know do a false

then it will be interpreted as a failure um so you know if i put true here it will be interpreted

as true and this is not necessarily a make file thing this is a bash thing just like the command

just like the command line. Let me show you real fast just just to prove it. I’m gonna prove it to

you. Okay, so hopefully you remember if you can do echo dollar sign question, it’ll tell you the

return code of the last command that you executed. So if I type echo hello, and then as another

command, I echo the return code of the previous command. Now it’s going to basically tell me

echo and then it’s going to tell me whether or not echo succeeded. You see a zero indicating

succeeded it would be really weird if it didn’t succeed but you know whatever now i can modify

this command just to type in some random stuff like what if i type in this notice how the second

part the echo return code part echoes a 127 anything but a zero is considered a failure

usually so it’s telling me 127 and that’s sometimes a standard code meaning something

wasn’t found i could also just do true and that should give me a zero because you know in bash it

bash it understands boolean so it’s like true success that’s a return code of zero it’s weird

but that’s what it is if i return if i execute false then the return code should at least not

be zero it’s probably going to be a one yeah there we go so just letting you know if we just

execute the statement false by itself or true by itself then that’s telling us

um that’s sort of like simulating a failed compile command or something i guess we could put

commands in there if we wanted to but i don’t really want to run the compiler just for this

in the compiler just for this so then I’m gonna do echo this echo statement

should be reached as previous errors which I’m using false to represent should

be ignored so now why how do I know that we’re gonna ignore the errors because I

put the little minus in front of the false notice how before it was either an

at symbol or nothing at the beginning of a command remember the at symbol does

Remember the at symbol does suppression.

The minus symbol says try to run this command,

but if it fails, ignore it,

continue with the recipe for that build target.

You know, recipe meaning all of the commands

that you want it to execute.

So that means the ignore failures target

should actually fail twice and then make it to line 84

where it just prints that message.

Then we’ll do, well, we’ll mark this as a phony target.

practice to try to mark your phony targets i’ve i’ve not done that in the past and eventually

regretted it a few times so then i’m going to do stop on failures is another target and we’re going

to do false whoops no minus because we actually want to stop on failure false and then we’ll do

another echo and we’ll say this echo statement should never be reached as previous errors

false should cause build build termination okay I’m kind of verbose but I hope this explains it

we’ll name this as a phony target too phony you’re a phony failures okay so we can run both these

maybe I’ll put a comment here just to show you it’s two separate things I kind of like doing that

we can run ignore failures and then we can run stop on failures separately to see what happens

Let me get rid of this terminal window.

Okay, so I’m going to do clear and, what was it?

Ignore failures and then stop on failures.

So ignore failures, notice how it prints false

because I forgot to put an at symbol in front of it.

And then the make system itself says,

hey, error, that command failed, error one.

Remember before we had the return code one

when we just typed in false.

me that it received return code one from trying to execute false inside of bash so it detected

that a failure occurred so these two lines go together then the same thing again because i have

two false statements in there and then the last statement says this echo statement should be

reached as previous errors should be ignored because we use the minus symbol i’m having a

hard time selecting text here i gotta i gotta go home and practice my text selection actually wait

So notice how, you know, the minus symbol right there says, let’s ignore.

So now let’s do stop on failures.

Stop on failures.

Notice how the first false showed up as an error to the make build system.

And then make just said, we’re done.

We’re not ignoring this error.

We’re just done.

So keep that in mind.

Anytime you have commands that need to stop the build, if they fail,

build if they fail you should not put a minus in front but anytime you have commands where

everything just needs to be executed even if some stuff fails you want to put a minus in front of

there we’ll talk about a clean target a little bit later which usually you know just continues

even if there’s a failure so we got silent commands ignored commands or ignored failures

and failures that we’re actually going to stop so now we’re going to get into the the meat and

make file it doesn’t really do anything it doesn’t build anything for us I’m just kind of showing you

you know how to work with the make file like the very basics now let’s actually start to build

things so the first thing I’d like to do is I’d like to make a target for the build directory

if you you know again if you just kind of like look at my file system here I don’t actually have

a build directory right so I want to make sure that a build directory exists I could of course

dir and then type build but I want the build system to do this automatically for me and also

as a lesson for what we’re doing so you know be sure the build directory exists so I’m going to

name a target and this is not going to be a phony target I think this is our first non-phony target

right yeah oh I forgot to put la default as a phony might regret that one day the defaults

you is phony and everything’s funny. Okay. So now we have our first real build target. I’m just

going to name the thing that I want to build. And I’m going to name it by typing the build directory

variable. I could type, you know, underscore build or whatever it is that I put for that variable.

What did I put there again? Underscore build. Yeah, I could put that if I wanted to. But again,

try not to hard code things. It’s better to stick things in variables if you can.

So since this is a target, I’m going to put a colon. It doesn’t really depend on anything,

because if we’re just talking about building that folder all we really have to do is execute a command

to just make sure that the folder is created right so i’m going to do no dependencies and

then i’m going to like maybe do a little all say function call just to announce that we’re going

to ensure the build directory a lot of people don’t do this but i’m going to do it ensuring

the builder and now i could put you know what the builder is probably a pretty good idea so that i

probably a pretty good idea so that I can see what I’m trying to ensure let’s

just run that real fast if I say oh shoot I guess I would have to do make

and then name build let’s see if that actually works yeah okay so I’m naming

the build directory you wouldn’t normally actually name the build

directory in the command line but I’m doing it so it says we’re ensuring the

build directory and then now it says the build thing is up to date because the

because the target finished executing,

but nothing actually got created, right?

So what I would like to do before we actually create it

is replace this builder variable with a special variable

that GNU make gives you called $at.

The $at variable, and I’m quoting it,

you don’t have to quote it,

it always ends up being the name of the current build target.

So again, it’s a bad idea to hard code

It’s a bad idea to hard code lots of different values

and it’s way better to put things into a variable.

But at the same time, if you can get away with

hard coding a variable name multiple times

and you can use something more generic,

well then that can also increase the power

of what you’re doing, make things easier for you

and easier for you to debug.

Save yourself some time, right?

Because coding is already hard enough.

So if I do dollar sign at,

it should show up as whatever the builder contains

because that’s the current target.

So let’s run that one more time.

that one more time and we’ll say clear and make build notice how it says ensuring the builder and

then it quoted the current target which is underscore built nice so we got that now let’s

actually run a command to create the directory that’s just going to be you know in linux the

command mkdir to make a directory and again i could put a builder here but it’s probably a

special variable that will change because what if later on i want to change the name of that variable

uh if i did then i’d have to change it in three places now but uh you know and possibly way more

places later down the line but if i use that special you know dollar at symbol then i only

have to change it in one spot okay so mkdir so that basically says it’s going to look to see if

that folder is created if it’s not then it’s going to create it with that command so if i do that

So if I do that, notice how it executed that command and also printed the command.

So it’s like mkdir build.

If we list the directory again, notice how there is a build directory.

Okay.

You go inside of there.

Everything’s fine.

I’m going to remove it real fast.

Whoops.

Okay.

All right.

our make file until it’s somewhat decent it’s not going to be amazing but it’s going to be somewhat

decent okay so we have a target that will build the build directory now let’s make our first real

target that is it’s going to be a phony target but it’s going to it’s going to do something for us

so run the program we’re going to make a target called run and so if you type make run then it

which in my opinion would be building the program first.

Your opinion may vary, but you know, so I’m going to do it this way.

I’m going to say run colon because that’s the name of the target.

And I’m going to say that the run target depends on the build target.

So remember this means that before run can actually execute its steps,

it will go quickly to the build target and execute all the steps in its recipe.

So essentially I’m saying before we try to run, let’s build.

So it depends on build.

By the way, build is not created yet.

We’re going to do that in a second.

not created yet we’re going to do that in a second the only line in this recipe that i’m going to put

is just the name of the binary meaning after build is finished running i can assume that

the entire executable is actually built and it’s ready to run so then i’m just going to run it so

it’s just like first go build it somewhere else and then run it and again indirection and modular

thinking is is a lot more powerful than just sticking all the build commands right inside of

run target and duplicate all those commands for the build target so this is way better and then

i’ll name run as a phony because this is not an actual file i’m trying to build the run the there’s

no file named run then we’ll do the build target build and link the object files and executable

okay and this is where it gets a little funny so we’re going to make a target called build

actually build everything we shouldn’t hard code them here we should treat every source code file

as as one separate thing that needs to be built every corresponding object file that comes from

a source file should be one separate thing that that gets built from the source code that needs

to be used to build i guess is what i should say so what should the build target depend on like

before we can say that build is satisfied.

How about just the executable file itself?

So I’m going to say the bin.

So our main program,

it should be totally built and finished

and set up and created and everything

before we can say build is satisfied.

And then hilariously, once that’s satisfied,

then the build target is totally satisfied too

because it doesn’t really do anything.

It just asks, you know, this is more of a shortcut.

Okay, so now let’s,

Let’s maybe first before we link let’s start writing commands to actually build the source code files. So I’m gonna do

build the first object file

So remember we have three source code files here. We’ve got first dot CPP and

Second dot C and third dot ASM. So that’s just three source code files. They should map to three object files

Let me let me draw this for you for a second before we talk about it further because I want to make sure

that what I’m about to do is crystal clear when we’re talking about like the dependency graph.

Okay. So my mouse is all over the place. I gotta execute a command real fast.

I have many monitors going on right now and the pen just keeps forgetting that it’s only

supposed to be on this one monitor. Okay. So I got that. Okay. So the first thing that we

executable called main.

So I’m just going to draw that on the graph here for you Windows people.

You can imagine there’s a dot exe on the end of it, but in, you know, in Linux and

other link and other operating systems, we don’t really need that.

So main, we want to build that.

That’s the, that’s the primary thing we’re trying to build.

What does it depend on though?

What needs to be built before we can, we can create our executable?

Well, we need to compile every piece of source code into a corresponding

all the object files will be linked together into the executable so maybe i’ll i’ll write

down first what the uh what the source code files are going to be so i’m going to do let’s see first

sorry about my writing i don’t have a smoother right now first dot cpp and i got like a really

small draw pad um i’m i’m making lots of excuses but i think probably my penmanship in real life is

maybe even a little worse i don’t know i was at a job a long time ago and i was taking down

names of customers it was like a tech support center and my boss after he saw the paper that

i was writing down he just looked at me and he went chicken scratches and then he walked away

okay so we have first dot cpp and then we have second this is why i like typing second dot c

okay so we got that and then we’ll have third dot asm so third maybe i should go slower a

At least I’m not making misspellings.

I know these are simple file names, but it could happen.

So we have three source code files.

They don’t really depend on anything because they just are themselves.

We don’t need to build these source code files.

So we’re not going to draw any arrows indicating that these files depend on something else.

They’re kind of like the primary source.

Like these are the things that would actually go into your Git repository,

whereas the object files that we’re going to generate and the executable that we generate,

they would definitely not go into your repository.

your git repository. Okay, so what’s going to happen? First.cpp is going to generate first.o.

We’re going to type that out in a second, but basically first.o. That’s awful. That just looks

disgusting. I’m going to keep it though. Okay, so in order to indicate dependency,

we’re going to draw an arrow from first.o to first.cpp indicating, all right,

if we’re trying to build first.o, then it will look at first.cpp and it’ll check.

check does it exist if it doesn’t then there’s an error if it does exist then then the the the

build system is actually going to check the modification dates of first.o and first.cpp

assuming that a first.o already exists it’s just going to check it’s going to say

does is first.cpp newer than first.o if yes then that means i have to recompile first.o if no i’m

not going to recompile it and i’m going to save a bunch of time this is kind of the heart of why

So same thing for 2nd.0.

2nd.0, I’ll just circle it here, and we’ll say that it depends on 2nd.c.

And then, of course, the 3rd.0 object file, it’s going to depend on 3rd.asm.

So again, notice how they don’t really depend on each other.

Maybe if you have a C++ file, maybe if you have a C++ program that’s really complicated,

maybe your first.o, it would depend on the CPP file,

and it might also depend on some other header files that you have set up somewhere.

So then there would be more circles and more arrows that we draw.

But for now, it’s just a simple program, so I’m just going to leave it this way.

But just keep in mind that when we name dependencies in the make file,

you want to name a dependency of everything that a source file depends on.

So like first.o, it’s definitely going to depend on first.cpp,

includes that are custom or from someone’s library and not the C++ standard libraries,

then you would want to say that first.o depends on those as well. Because if you change

a header that you made elsewhere in the program, then you probably want to recompile

that corresponding object file, right? So just keep that in mind.

Anyway, we’ll do first.o. Okay, so now the executable program main, that needs to be

all the object files so I can just say that it depends on all three object files

with just this right here and this is the basic idea of what we’re about to

type up in the make file itself keep in mind that the make system will actually

build this graph in memory under the hood to try and figure out which things

actually need to be built or not you could even launch make with multiple

threads I’ll probably forget to do it but it’s a things like dash J and then

things like dash j and then the number of threads you want to launch in order to totally saturate

your cpu and have as many things compiling as possibly can be compiled make will figure out

which things don’t actually depend on each other and then build them in separate threads at the

same time so your build system is just like so much faster so let’s see we got uh corresponding

object dial for every source file and then we got the binary that depends on the object notice how

source code file what would happen is when we wanted to build the let’s say we wanted to just

you know this is the the target the binary target that is just named main what will happen is it’ll

look at its dependencies and the way we’re going to write it it’ll say okay it depends on the three

object files if they’re already there and they’re already satisfied then it it won’t actually

if they’re already there and they are all newer than their corresponding source files then none

corresponding source files, then none of them will get rebuilt. If main itself sees that it is newer,

like the existing on disk version of main is newer than all the object files, then that tells the

build system, oh, I don’t actually even need to relink the binary at all because the object files

didn’t change, which means the source code didn’t change. And so you could actually like type make

build and nothing happens because nothing needs to happen. It’s going to be pretty cool. I’ll show

I’ll show you in a moment.

Okay.

So for now, I’m going to try not to erase this.

I’m going to do a toggle.

Do I can, I don’t know if I remember, I think it’s like control.

Let me do the menu here.

I don’t want to screw it up.

Okay.

It would have been control.

Okay.

So I’m going to toggle visibility.

Just get that off there.

Just in case I want to show it to you again later.

So now let’s make the first object file.

So remember the first object file is first.o.

to create a target that will build the object file based on the first source code file.

I’m going to put a colon there to indicate that it’s a target.

And then remember on the right side, we can do dependencies.

Well, we have to say that this depends on the first CPP file.

If we didn’t, then every single time we run the make system,

then this object file is just going to get built over and over and over again,

even if it doesn’t need to.

So now with a dependency there, it’s going to look at first.cpp

and then it’s going to look at first.o if one exists if one doesn’t exist it’ll

definitely build it but if one does exist it’ll go is first.o newer than

first.cpp if it is don’t do anything but if it’s not then rebuild it make sure

it’s fresh here’s the other thing though I kind of wanted to build everything

inside of the of the build folder so I’m gonna add the variable build in front of

it let me do it first without doing that so that you can kind of see what will

So it’s going to depend on first dot CPP.

And then there’s another thing you can do in make files where you can,

I forgot the proper name for this,

but you can specify other types of dependencies where they only need to exist

as a dependency.

They don’t necessarily need to be freshly fully updated because,

you know, the way files and folders work,

if you start adding or removing files from a directory,

then the directory thinks that it’s been updated.

And so that can kind of throw off the build system’s thinking

the build system’s thinking because your build directory will always think that it’s been updated

if anything happens. But we don’t really care about the date of when the build directory was

built. We just want to make sure that it exists, right? So we put a little or bar and we put those

types of dependencies on the right side of the or bar. Okay, maybe I’ll leave a comment or something

at a later date specifying the official name for that, but I mean, that’s how it works.

So that’s the recipe.

It’s got dependencies.

You know, in the past, I’ve said when you name a dependency,

it goes off and make sure that that dependency is resolved.

Well, that’s true for phony targets and targets that sort of do other things.

But in this case, if we’re just naming a file, all it’s going to do is just look

at the modification date of the file.

So now let’s do the recipe.

I’m going to do CPP, which means that’s the C++ compiler.

And I could write, you know, G++ and all the commands here directly

directly if I want to, but it’s kind of a waste of time.

And it’s not, it’s not very smart because we said before, what if you want to change

the flags and you have a hundred objects that you’re building, you probably just want to

be able to change one variable instead of a hundred different things.

So we’ll do CPP flags and then we’ll, let’s see, we’ll take first.cpp as a source.

You can imagine again, this is like, you know, G++ happening inside of here and then wall

inside of here.

and then at some point you specify the source.

Okay, so just keep that in mind.

And then after I specify the source,

I want to specify the output

and the output is going to be first.o.

And now let’s use another trick

which allows us to make slightly more powerful make files.

So again, you want to avoid hard coding stuff if you can.

There’s a special variable.

Well, okay, let’s go back first to the other special variable.

Remember $and, or sorry, $at.

that always refers to the current target right so first.o is the is the current

target so I don’t even have to type first.o here I can just do dollar sign

at there’s another special variable which just basically means the first

dependency that’s been named so if I just do quotes and then I say this it’s

weird I know dollar sign and then angle bracket pointing to the left or like a

typing first dot CPP. If you wanted to send a bunch more stuff, like if you wanted to

send all of the names of all dependencies that you named into the command line, then

you can use the carrot like dollar carrot. That’ll do all of the dependencies. But if

we’re just compiling one source code file, you probably want to just do one dependency.

Like for example, if I had other thing dot HPP as a dependency, I probably wouldn’t want

because then that would be telling the compiler,

here, I want you to compile first.cpp and also other thing.hpp,

which probably wouldn’t work.

It’s probably not what you want.

So instead, if I do this like arrow to the left,

then it will only give it first.cpp rather than both dependencies.

But I guess we’re simple.

We don’t really need all that right now.

So we got that finished.

Let’s see.

We now have the binary.

okay this is only going to run if i do make first dot oh maybe i should do that right now so that

we can we can take more baby steps to making this more complicated so i’ll do here and make

and then i’ll do make first dot o so notice how when i typed first dot o it went right to this

target no problem and then it started to you know it looked at first dot cpp just to see you know

built or not and it it also looked at the build directory as a dependency and

then finally it actually compiled so what we should see right now is first

dot o sitting in the directory and then also we should see the build directory

again let’s just double check that so if I list everything notice how the build

directory exists if I go remove and then remove the build directory and then

clear it again notice how we also have first dot o so I can say remove first

So if we go clear and list everything again, now I’m going to do one more time.

Make first.o and then if I do list, then the first.o got built and the build directory got built.

Notice also that as soon as I ran that command, it did make dir on build.

So it built the build directory.

So that was the build dir target.

And then it went over to the first.o target and it executed that command to actually compile it.

All my variables expanded, so it’s like G++, wall, pedantic, whatever.

That special variable with the arrow pointing to the left was the first source code, or source code file.

And then the $at variable expanded to the current target, which is just first.o.

If we run this again, I’m not going to clear it, I’m just going to go make first.o.

Notice how nothing actually happens.

It’s missing two lines.

Up here we had two lines where we made the build directory

and we also compiled the object file.

But notice how here it just says the object file is up to date.

It did not recompile it because it can tell that the source code was not changed.

Let me go into another directory right here and I’ll just modify the source code file.

I’ll just add an extra line to it and then I’ll go back here.

and if I compile it now we should see that it chooses to compile it again

because now the source code is newer than the object file so if I go make

first notice how it did not build the build directory because it already

existed but it did rebuild the object file because the source code was updated

if I immediately try to build it again it does not need to rebuild it because

the source code didn’t get updated since the last time I built and so it says

it’s up to date up to date sometimes you might see a message that says something

just says something like nothing to do, I think.

So just keep that in mind.

We’re getting a little bit better now.

But I’m also making a mistake here

because we have a build directory

but there’s nothing inside of it.

I wanna put everything inside of my build directory

just to show you how annoying this could be.

Notice how we got a first.cpp and a first.o.

Imagine if you had a million source code files,

you’ll have a million object files

sitting in your repository or your directory

kinda cluttering the place up, right?

cluttering the place up, right? I don’t like it. I’m not a fan. You probably want to edit your

git ignore so that you don’t accidentally put object files into your repo. But for me, I think

it’s better to just ignore, git ignore a build directory, and then just stick everything inside

of the build directory. It’s up to you, but this is the way I’m going to do it. So how do we get

it to be inside of the build directory? First, let me remove the object file. Okay, let’s see,

I think I can get rid of that. That’s responsible for the swap file. We probably want to put

star.swap inside of gitignore so that the temporary files from nano don’t go into our

repository. So now I’m going to prefix this object file. And a lot of people don’t do this,

but I think it’s pretty cool. I’m going to do build dir slash the object. And then now

everything’s going to show up inside of the builder or at least that one. So if I remove

directory just kind of list things again then I can do make first oh and then

notice how it mmm what happened oh what did I do wrong well I did something wrong

okay hang on let me see if I can fix that first oh what in the heck did I do

second and third. Okay, so I didn’t break anything.

First.o. Oh, because when I typed this on the command line, I was typing first.o and

well, that’s not the target anymore. The real target now is underscore build first.o.

So maybe this is a good time to stick our binary target inside of there.

So we have the binary target. I kind of ignored it for a second because I wanted to show you an object file first.

We want to make the main file that gets executed.

We have to link everything together.

So, well, maybe this is a better description down here for that.

And then up here, I’ll say alias for just building the binary.

Whoops.

Binary building.

Building.

There we go.

Okay.

So now we’re actually going to do it.

So we’ll put bin.

So now bin is its own target, meaning our main program that we want to create

main program that we want to create is uh it’s going to happen somehow the uh dependencies that

it needs though should be all of our object files that we intend to create so first you know one of

its dependencies should be uh inside of the builder and i know this is annoying to type these variables

all over the place but kind of the price you pay for having everything outside of the working

directory but i guess that’s why a lot of people don’t do this we say builder first dot o so

So basically saying before we can create the executable, we have to first build the first

object file.

And then we’ll just name more dependencies.

I’ll say the builder, we separate dependencies using spaces.

That’s another reason that make has problems with spaces if you’re going to do like an

absolute path or something.

So then we’re going to grab the second object file as a dependency and then the third object

file as a dependency.

means we just need to set up targets for each one of those object files we only have first.o

set up so far we’ll have to set up the second and the third one so aside from those dependencies we

want to make sure that the builder is actually created so we’ll put builder as another uh you

know non-time stamped uh dependency like we did with the object file then the command we’ll actually

and remember CPP is just GCC and then the link flags is the stuff that we set up up above

which is basically going to be you know wall pedantic G and then M64 no pi no you know all

the stuff we don’t want to type manually at the very bottom so we’re going to do that link flags

you know we want to give it every single object file as an input so we’ll name you know first.o

and then we’ll name second.o second.o and you know i’m getting irritated i hope you can tell

already i don’t want to type all these things so we can use a shell trick we can put a wild card

there we can say how about i just give you every file asterisk that ends in.o so star.o basically

on that command line it’s going to go look into the build directory and just

give it every object file that it sees it’s not a great idea if you have

multiple different executables that you want to make and they’ve all got their

own main functions and they’re all in the same builder but in our case we only

have one executable with one main function so we can just throw all of the

object files we want at it if you had other types of object files you wanted

to link into your executable like for example shared object files you could

object files you could basically do the same thing but just go you know builder star star.so

or maybe not even in the builder if i if let’s say if i gave you a shared object file

and i wanted you to link that into your program you would just specify wherever it was

you know maybe it’s in the root something.so maybe it’s in a different folder um and maybe

you want to create a variable up top to just kind of like name where the shared object files are

libraries are it’s up to you but I’m just letting you know this is how you

would do it to get a shared object file in there as well for this program it’s

very simple we just are gonna grab the object files and that’s it so now we’re

just ready to specify the output file with a dash o and we’ll use that same

trick again dollar sign and at so we don’t have to specify the the full

target which is just going to be the bin variable so let’s see what do I got to

If we run it right now, it should fail because it’ll complain that we don’t have second and third built.

But at least I have enough now to use the make run target.

So we can stop specifying the names of files and just say something like make run or make build and it’s just way easier to type.

So I’m going to type make run.

It’s going to depend on build.

Build is going to execute before run even tries to run.

Build is going to say, well, I depend on the binary, the executable.

Then that means the binary’s target is going to run.

run it’s going to depend on all these object files so before it even tries to do anything

it’s going to go down here to the first dot o object file it’s actually going to build it

then it’s going to come back and try to handle the next dependency which is second dot o

but there’s no target for second dot o and it’s not on disk um so it’s just going to fail at that

point so let’s see if let’s see if i’m right we’re going to do make run clear and whoops

Make run

Okay, so ensuring the build directory. Oh, I forgot to mention that yet

It’s gonna ensure the build directory then it actually builds the first object file so far so good

Then it complains no rule to make the target second auto so we can’t continue Oh

drat

By the way just to show you I think it’s like a J

Let’s say we wanted to launch this with eight threads. I think it might be low

Is it lowercase or uppercase J? I can never remember. Let me try lowercase

second how about uppercase nope okay so it’s lowercase well I guess it just fails right there

I was kind of hoping it would try to spawn off two threads to look at second and also third

at the same time and then fail twice maybe that’s why it didn’t do that because it automatically

saw right away oh we can’t do anything so let’s just cut the whole build process so now I’m going

Cut cut cut paste paste and then I’m going to say build the second object file

So we’re going to depend on seconds

There are actually advanced patterns you can use so that you don’t even have to specify the object files you can

There’s there’s these things that people do I did it a couple times

But I don’t really like it where you can have make sort of scan your source code directory for all CPP files and

Automatically compile them in the exact same way and then the output file is like

And then the output file is like the name of the source code file and then.o concatenated at the end of it.

But this video is not like that advanced, so I’m not going to do that.

So we’re going to compile second.c into second.o.

And we need to make sure the build directory is created.

And instead of using the CPP compiler, we’re going to use the regular C compiler.

And then we’re going to do cc flags right there.

and I think that’s all we need so then we’re gonna do the last object file by

the way in nano I’m going control K to cut and control you to paste and it’s

kind of convenient if you want to like duplicate you know a few lines we’re

gonna use the ASM to assemble the assembly code so I’m gonna do third dot

what it was and then we have to use a totally different command now we have to use the assembly

assembler variable so that’s the name of the tool that assembles and then we’ll give it the assembly

flags i mean i guess it’s not that different and then we’ll say give it the first dependency which

is just the source code and then output to the at target so we should have like a lot of stuff

ready to go let’s see if this works now if i say clear and make run then what is it going to do it

then what is it going to do it the first thing it does is it compiles the second object file

notice how it did not compile the first object file because remember we just compiled that

and we didn’t change the source code so it just ignored it and we have a faster compile

then it assembles aka compiles the assembly to its own object file and then

now that all those object files are satisfied notice how like you know for the binary

all of those object files first and make sure the build directory is there. Then finally it can

execute its own recipe, which is just one command saying, you know, let’s link this stuff together

by grabbing all the object files. So that’s this line right here, the linking flags and the linker.

So it’s like GCC and all the linking flags, the no pie and all that stuff. And it’s expanded that

variable. Remember we put builder slash star dot O. So that glob right there basically says grab

glob right there basically says grab all the object files from the build directory and then

output the main program in the build directory no no i think even if you’re using a build directory

sometimes people will still put their executable in the root i don’t know it’s up to you i don’t

really care finally after all that is done you’ll see let’s see what we actually did is we said let’s

its recipe it goes down here for build and then build says first we need to build that binary

and then binaries like first we got to build the object files and then you know all those

get built finally when the object files are built then the binary gets to link itself

when it’s done linking itself then we go back to this point because now finally the dependencies

on line 108 are satisfied then that means the build target its dependencies are satisfied

dependencies are satisfied. It would execute its recipe, but there’s no recipe there. So it just

goes back to run because in the first place, run said, I need you to make sure that build is handled

before I’ll execute my recipe or all of my statements. So finally, after all that stuff

was done, then it’s just a command line that just names the executable, which is going to run it.

And that’s this last part right here. So right there, it just names the executable using a

using a whoops using a relative path to our main executable so now below that

line the program is actually running it’s like hello from first hello from

second hello from third and watch this if I do make run again nothing happens

except executing the program notice how none of the object files are getting

built the binary is not getting linked nothing is happening because it’s

already done all it does for the run target now is just run the executable

run the executable. It’s the same thing as if we did build main, just to run it. That’s it.

You know, of course, it’s a little more proper to go dot slash, right? You don’t have to use the

dot if the thing you’re naming is in a different directory, but you do if it’s in the same directory.

Like if we just went into the build directory, by the way, here’s all our build files. They’re

in one convenient place. You can do a git ignore on that, or you can just delete the folder if

if you want to clean everything.

But we can’t just type main here, it’s not going to work.

We have to do a dot slash main because we’re in the same folder.

But if we’re one level higher, then we can just name build main and it’ll work.

Okay, so let’s do another thing real fast here.

Let’s do another target that is kind of typical.

It is the clean target.

useful when people have object files just scattered around the root of their of their file system

there their repository but i like to add it in anyway for mine even though mine’s a little bit

cleaner right now so we’ll do another target called clean and we’ll just say clean the uh the

build area system thing stuff and i’m just going to put the word clean with a colon no dependencies

meaning it’ll always run right away and not depend on anything it’s obviously a phony target because

It’s obviously a phony target because I’m not going to make a file called clean so I’m

going to stick that as a dependency to phony.

And then what is it going to do?

Usually this is where you put something like let’s remove star dot o, let’s remove all

of the object files and then let’s remove the binary file and let’s remove anything

that we built so that we can just clean it.

Maybe I could put that here, build dir slash o, I’m going to make it a little bit better

in a second.

inside of the builder and then we’ll also remove the binary.

So when we go back into our file system,

if we list the build directory,

we should see that it’s empty after we run this.

So I’m going to do that right now.

I’m going to do make clean.

And then you can see that these two removal commands got executed.

And then if I list the directory again, notice how it’s empty.

But the remove command will fail

if it tries to remove something that doesn’t exist.

to remove something that doesn’t exist so we’ll actually get like a a nasty message from clean

or from make if we try to clean again so we say make clean it says remove tried to remove object

files but there was nothing inside of there no such file or directory so then it just stops the

whole process well that seems okay for now but what if you uh what if you had gone in and removed or

and that stopped other things from being cleaned you’d probably end up being

pretty frustrated right so for the clean target I’m just gonna say remove is a

command that won’t stop the build or the target so I’m gonna put a minus in

front of there meaning don’t stop if something goes wrong just keep

executing all these commands and so now if we do clear and make clean notice how

it actually tries to remove both types of things it doesn’t just stop on the

I guess this is the first problem.

It’s trying to remove the object files.

Then remove says, no, I can’t do that.

There’s nothing there.

And then make says, I’m ignoring you.

Stop whining.

There is no bathroom.

And then the same thing happens for removing the executable main.

But we can make this actually a little bit more efficient.

I mean, this is kind of what you’ll do if everything is sitting at the root of your repo.

But if we simply have a build directory and you’re absolutely sure that you already have

you know set to the correct value then you could just remove i don’t know if i want to do it now

because i’m getting scared you could just remove the entire build directory i’m going to do it but

i’ll explain to you why i kind of don’t want to do it in a second we could remove the entire build

directory so like with the remove command remove recursively the entire build directory

just so you don’t see it if you feel like it’s an eyesore so i’m going to do make clean

do it again do it again do it again and notice well why wasn’t it

i feel like it should have failed but i guess remove felt like it wasn’t a failure if build

didn’t exist but notice how the builder is gone so i didn’t really have to specify

the object files but it’s a little bit safer i don’t know it depends on your philosophy

this is way more compact and nicer that was my first feeling but it’s also a little more

there into the variables area and i like change the builder somehow i change its name or i change

its value and i accidentally now have this variable pointed to like a dangerous path

and then if i type make clean it just erases everything not sure i really like that idea so

i’m going to do i’m going to go back to the original way which takes more typing

remove every object file and maybe before that let’s remove the binary so if we do

if we do make build which by the way will only build things it won’t run anything remember

the dependency graph that we looked at a little while ago or not the dependency graph that we

explained the dependencies so it does everything required to build but it doesn’t actually run the

program and that’s fine i just wanted to go look inside of the build directory

and you can see it’s got all the stuff there so then if i just run make clean one more time

by the way, if I try to run make clean from here,

there’s no make file in this directory.

So nothing’s going to happen.

So I’m going to go up one level, make clean.

And then if I list the build directory,

there’s just nothing inside of it.

Okay.

All right.

Let me just look at my notes right here.

I just finished building this whole giant make file.

It’s not even like that complicated,

but I hope that you’ve learned a lot

about making make files at this point.

this point i hope you kind of like the idea let me just double check there’s nothing else i wanted

to say for this video oh i should point out that gnu make is not the only build system available

it’s just the one that i like the most um remember you can also put shortcuts in there like any

command line that you could actually type could be part of the recipe of a target it doesn’t have

to only build it doesn’t have to only be build targets so again you could make commands in there

like deploy your executable somewhere or check something or like a long time ago I used to have

a recipe actually it was that program I was talking about at the beginning where I converted

an hour-long compilation to just like a minute I had another target in there called deploy so I’d

say make deploy and what it would do is it would build and if it built then it would run unit tests

on my code and if those succeeded then it would copy my source code to one of my other servers

my other servers then remotely it would tell that other server to build and run tests and if all

that succeeded you know if it didn’t succeed then it would just quit if all that succeeded then it

would shut down the the server that I had written as a running service and then it would copy the

freshly built executable to the right location and then it would restart the service so basically I

just like do a bunch of coding and then I you know run my test if everything seems cool then I go make

five minutes or whatever it took to run all the tests and do another compile and deploy everything

and my server was just totally updated just by that so there’s a lot of stuff you can do but

there are other great build systems out there there’s one called gradle there’s one called

maven there’s other like node.js systems there’s a ton of build systems out there

probably a slightly more popular popular c++ build system is called cmake it’s more tailored to c++

um but you know you obviously don’t need to do it it’s just i think like a little bit easier

depending on what you want to do so keep that in mind this is not the only system there’s a lot

out there to explore but this is this is definitely a great first step um let’s see what else can i do

oh we showed you the diagram showed you the info function and the targets and the dependencies

and the run build well i guess i’m out of stuff to show you that’s that’s all i had written down

that I wanted to show you maybe I could this is not a git video I’ll make you

know I have other videos forget but you know if this is a git repository you

would nano git ignore just because I kept mentioning this we’ll do nano git

ignore and then I’ll just say ignore the build directory and ignore swap files so

I’ll say like ignore the build directory and ignore anything that’s edited and

your git status and git add and git commit and stuff then the build directory wouldn’t even show

up so yeah kind of i guess it’s kind of unnecessary for this project okay i think that concludes this

video assuming i was actually recording this entire time thank you so much for watching i

hope you learned a little bit of stuff and had a little bit of fun i’ll see you in the next video

practice practice practice and start making your own make file videos and let me know

files in your own programs and let me know if you liked the content of this video and like if it

helped you i love receiving emails way after the fact when people say hey i love make files now

and it’s all because of you and i do it all the time because then i get to sit alone in my dark

room and giggle to myself it’s all because of me um okay i think that’s probably a good note to

hey everybody thanks for watching this video again from the bottom of my heart I really

appreciate it I do hope you did learn something and have some fun if you could do me a please a

small little favor could you please subscribe and follow this channel or these videos or whatever it

is you do on the current social media website that you’re looking at right now it would really mean

the world to me and it’ll help make more videos and grow this community so we’ll be able to do

videos, longer videos, better videos, or just I’ll be able to keep making videos in general. So please

do me a kindness and subscribe. You know, sometimes I’m sleeping in the middle of the night

and I just wake up because I know somebody subscribed or followed. It just wakes me up

and I get filled with joy. That’s exactly what happens every single time. So you could do it as

a nice favor to me or you could you could troll me if you want to just wake me up in the middle

of the night, just subscribe and then I’ll just wake up. I promise that’s what will happen.

Also, if you look at the middle of the screen right now, you should see a QR code which you can scan in order to go to the website

which I think is also named somewhere at the bottom of this video and

It’ll take you to my main website where you can just kind of like see all the videos

I published and the services and tutorials and things that I offer and all that good stuff and

If you have a suggestion for

Clarifications or errata or just future videos that you want to see

please leave a comment or if you just want to say hey what’s up what’s going on you know just send

me a comment whatever i also wake up for those in the middle of the night i get i wake up in a cold

sweat and i’m like it would really it really mean the world to me i would really appreciate it so

again thank you so much for watching this video and um enjoy the cool music

as as i fade into the darkness which is coming for us all

Thank you.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply