Lesson 07
Compiling the Game in the Command Line with a Makefile and the make Command
Summary
# Summary of the Makefile Lesson
In this lesson, we learned how to create a Makefile to streamline the compilation process of a program, specifically using the G++ compiler from the GNU Compiler Collection (GCC). Traditionally, compiling each source file (CPP) and linking libraries through the command line can be tedious, especially for larger projects with many files. Instead of listing all files manually, a Makefile automates this process.
### Key Concepts
- **Makefile**: A file containing rules and instructions to automate the build process. It defines targets, prerequisites (dependencies), and commands needed to build the program.
- **Targets and Prerequisites**:
- The target is the name of the file to be generated (e.g., the final executable).
- Prerequisites are the files that must exist before the target can be built (e.g., object files).
### Steps to Create a Makefile
1. **Define the Target**:
- For the game executable, the example target is `labyrinth` which depends on the object files `main.o` and `game.o`.
2. **Compile Object Files**:
- Each object file is generated from its corresponding CPP file using G++ with the `-c` option to prevent linking.
- Commands are added under the target line, and it's important to use tabs (not spaces) for indentation.
3. **Create Build Directory**:
- To keep the source directory clean, a separate `build` directory is created for object files and the executable.
4. **Automation with Make**:
- After setting up the Makefile, simply running `make` in the command line will handle the building process.
- Make detects changes in source files and only recompiles what has changed, rather than recompiling everything, which saves time.
### Example Makefile Structure
```makefile
labyrinth: build/main.o build/game.o
g++ -o build/labyrinth build/main.o build/game.o -lSDL2
build/main.o: main.cpp
g++ -c main.cpp -o build/main.o
build/game.o: game.cpp
g++ -c game.cpp -o build/game.o
build:
mkdir -p build
Conclusion
By creating a Makefile, you improve the build process, avoid cluttering your development directory, and allow for easier recompilation of files. This lesson emphasizes the importance of automation in software development and how tools like Make can enhance productivity.
Thank you for watching, and see you in the next lesson!
Video Transcript
Welcome back to another lesson. We are going to learn how to make a make file to improve
the compilation workflow or build process. As we have right now, if I want to compile
my program, my game, for my case, I'm using the compiler G++ from the GNU compiler collection
GCC. And every time I want to do it via the command line, I have to specify every CPP file
that I have. And I have also to link to the SDL2 library with the dash L option. And that process
will generate a file called a.out that I can execute via the command line so I can power up the
game here. Now imagine in a production scenario, right where you have an actual real world game
that's pretty big, there's there, there are going to be lots of files. And it's going to be, it's
going to be really painful to type all the list of files every time you want to compile it. Of course,
today, many, much of this process is already automated via GUI IDs and the such. But since we
are keeping it simple, and I'm building everything from the command line, I'm going to stick to a
command line tool called make. And we're going to have a file called make file. So the make file will
contain a set of instructions to tell. So it instructs, you give it a set of rules that will be used
to build the program. And pretty much is like this. So if you want to generate the executable for
your program, you need this object file, and you need that other object file, so on. In our case, we
can see here, if I go to the right hand, the left hand sidebar here, of my text editor, I have these
files of the extension dot o dot o, these are object files that are generated, right? When I compile
each of these individual CPP files. And for example, we have the game object game dot o, and we have the
main object, my main dot o. So in order for us to build the game, we are going to need game dot o. And
we're going to need main dot o. Now, each of those files, they need instructors, instructions as to how
to build them, right? And for example, for game dot, all, we need to compile it with g plus plus. So we
are going to give a command in order to generate the file game dot o. And the same goes for main dot o. And
these two files are requirement or prerequisite to the main, the overall target here, that is the game
program itself, the program executable. And that program executable requires the building of game dot
o and the main auto, the prerequisites. Okay. So keep that in mind, those words that I said, they were
intentional, all these keywords, targets, they have prerequisites or dependencies. And we have the commands in
order to build those files. Okay, with that, let's create a file. I'm going to call it the make file. Okay, I put the
first letter capitalized. So the make file. So what I want to do is improve my compilation and make this kind of
automatic. So whenever I want to compile, all I have to say is instead of typing g plus plus main dot cpp game dot
cpp dash lsdl two and so on, I just need to type make in it automatically look, look at the make file and build the
program for us. It also helps when you change a specific file. And it will detect that you only change that
specific file, therefore, you only need to rebuild that one specific file instead of rebuilding the whole thing over
again from scratch. So that saves a lot of time. So so the executable name is a dot out, this is just generic,
because I did not specify. So we want to give it we want to give our game a name. Maybe I want to call a lab ring. So
let's call lab.
Labyrinth, did I did I spell that right? Okay, so this is the target. So the target is the whole game itself. And this
target, I'm going to put a colon here, and I'm going to put a space. And this requires what main dot cpp game dot
cpp and a linking to SDL to well, to make the labyrinth, we need the compiled object files. And these will be the main
auto and game auto. Okay, these are the requirements to build a labyrinth. Now, if we already have those requirements,
which we will define how to make them later, we can go ahead and compile everything. Okay. So we are going to say g plus
plus now I have to give the object files. The way I did it before let me kill this was by just passing cpp. But now I just
want to pass the object files. So I'm going to look at the manual page for the g plus plus command, I'm going to say man
space g plus plus there's an option that we can use. So it takes the it will take the object file. Okay, so let me see
dash c option says not to run the linker. We're going to need this for when we're going to create the object files. That's one
thing. Let's see. Okay, so we don't need an option. I don't think we need an option for that. Just the C is going to be used
later. But we did see the dash all for how to make that specific file. So dash all is the output. Go back to the beginning. You have
the option dash all for output. That's what we want to rename a dot out to actually lever it. So I'm going to say dash all lab
labyrinth. And I'm going to put main dot oh, the end auto like that. Okay, so this is going to generate the executable called
labyrinth because of dash option output. And then it's going to take in order to generate the executable you need the main dot
oh and game dot oh, now we also need the linker. So don't forget at the end dash LSTL to
now in order for us to get generate main dot on game dot oh, we need to define a new target here like a dependent target. I'm going to
call it main dot oh colon. And we are going to specify whether it needs anything. What's the requirement for us to have main dot
oh, well, we need some file to exist. And this file is the main dot CPP file. So the requirement here is the prerequisite that the file
main dot CPP exists. Now the command to generate main dot oh will be g plus plus. Now we're going to use the dash C. So we do not link
anything we just generate the main auto file. And that will be main dot CPP like that. Now just do the same thing for game game
dot oh in order to generate this target, you need to have the file game dot CPP. It needs to exist. And to be to build game dot
oh, the command is g plus plus dash C. So you don't link you just generate the object file. And it's going to be sorry game dot CPP. Now, now
that we have this big file, let's try it out. I'm going to quit with Q from the man page. And I'm going to say make. And you can see it
executed first g plus plus main CPP, g plus plus dash oh, and so on. And notice I didn't I didn't show this one. And I think it's
because it's already generated. And it detected that it was already there. But you can see there's a lab reading factor to execute
will hear that I can see it executes the program. Okay, so you can see the compilation process leave these residual files
right the game dot oh, main dot oh, and so on. So it's kind of a mess here, having these files together with the actual source code. So
that's not a good thing. So we have to clean that up. And a good way to approach this is to create a new directory just for those built
files, you can call it build directory or the distribution directory dist, whatever you want. Okay, so I'm going to create this directory
called build. So I'm going to place all the dot oh files there. And maybe I also want to generate lab reading there so it doesn't mix with
the source code. Another common practice is to create a dedicated directory for the source code called SRC. You can also do that. But I'll leave
that to you to do on your own. Okay, now to be able to make the build I obviously could just create the directory and move the files manually
myself. But every time I run make it's gonna do the same thing again, and I have to manually move them. So what I'm going to do is I am going to go here in the
make file and change it. So it generates the targets in the build directory. Okay, so let's think about that. First thing North generate this thing
called labyrinth. This target needs I'm going to say at please generate it at build slash a reverend. And also, you want to take the main
auto from build slash main auto and build slash game dot oh
and that's one way but then it's going to go to the requirement here and the requirement it's going to create in the same directory maybe I can use
let me think about this. Maybe you can use a dash a little option here. And say build slash
main dot oh, it's kind of redundant. No, maybe there's a battery. And I think there is. And this ought to want to be big. Game dot oh, let me remove these object files
here that I have I'm going to say our star dot oh
and they're gone now from my current directory I must remove a dot out our M a dot out. Also going to remove library
and let's see what happens I think there's we also need to build stuff to create directory so it's not gonna be okay you can see there's an error
assembler messages build slash main dot oh, no search file or directory. So we tried to build
that. Okay, so we also need to create the build directory here. Let me think about this depends on main depends on game depends on so let's make it depend on
the build. And I'm going to define the build target to be no prerequisites for build but we have to make a directory I'm going to use mkdir build
like that. And I'm going to say make again. Let's see what we have now a brand g++ dash c main dot CPP dash build slash main dot oh, and the other ones. Let's see what's in the build directory
how game dot oh main dot oh and labyrinth let's say dot build slash labyrinth. And we see the game actually executes. So that worked.
Okay. I am just curious I think if we put the target. What happens if we do build slash main dot oh, and here I remove.
Never mind, you can try it on your own if you want to do it another way maybe there are all these optimizations we can make to the make file, make variables and so on.
Because as the project bill as the project scales, you're going to have more and more files, so you don't want to repeat yourself every single time so you have to make targets and it's pretty much the same kind of pattern.
So there are ways you can optimize the make file with us. So you don't repeat yourself as much but I'll leave that to you if you want to do your own own research and learn more about that.
Okay, so this is looking good. Let's see something is not right here. I see what's going on. You see, every time a run make it's rebuilding everything but that should not be the case why because I already built everything.
So it's not detecting that I rebuilt everything and actually we are going to have to refactor this never mind what some of those stuff has done before.
And the reason it's not detecting that I already built it's probably because my target is specifying like that instead of having the build slash.
So if I put build slash here main dot oh and I put build slash game dot oh and here labran build slash game dot oh what would happen.
So it didn't execute the other ones I want to remove the directory build. Let me see our M dash R. I removed the directory build.
And I want to see when I run make again. It made the build directory salon. And if I run make again now it detected that I already built main dot oh game dot oh but it did not detect labran.
And it's probably because I need to build slash labran here.
There you go. So now it detected that already had built labran the executable itself it says make build slash labran is up to date.
So that that that fixed that problem of detecting whether you had already compiled everything. So it will save you a lot of time.
Not having to redo the thing that's already built.
So say I changed game dot CPP for example I add some comment here and I save and I do make it detected that game was changed and it rebuilt game dot oh and then rebuild the labran note it did not have to.
Rebuild main dot oh because it didn't change game dot CPP was the file that changed.
So that's nice.
Let me revert this.
Let's have an overview what we did in this lesson. We learned to make a make file so that we can use the make command to conveniently compile the project and recompile it.
Whenever there are changes in the source code it will automatically detect changes and recompile the specific files accordingly.
That helps you save time instead of recompiling everything over again from scratch.
It just takes the files that changed and compiles those.
And then compiles the executable again.
To make to use a make file have to define targets that require dependencies or prerequisites.
The target is at the left hand side of the colon.
The prerequisites are at the right hand side of the colon.
Now under that entry you have to specify a command or a set of commands that will generate that target.
You have to press tab and then type the command under the entry with the colon.
Okay like we did here.
Make sure use like that.
This thing should be a tab not a space.
I don't think the space will work.
So make sure to use a tab.
And here we define build slash library if this target because we want to separate the object files and executable into its own build directory to not clutter the source code directory.
So we have the target on the left hand side.
This is the main one that will generate the executable for the game.
And we have the prerequisites.
The executable depends on the build directory existing.
And in order to have the build directory you have to make MKDR build here.
It will automatically detect whether it already exists or not.
Then we have the main.o file.
And in order to generate the file you have to look at the entry for main.o here.
This target build slash main.o depends on the file main.cpp existing.
And in order to generate build slash main.o we use g++ dash c so we don't link we just generate the object file main.cpp and I put a dash o so it can generate under the build directory build slash main.o.
Same thing for game.o.
Build slash game.o depends on the game.cpp file existing.
And the command to build to create the file build slash game.o is g++ dash c game.cpp dash o build slash game.o.
And with those prerequisites met and their generation already complete it will go ahead and finally execute the command in line two.
That will generate the executable build slash labyrinth g++ dash o build slash labyrinth.
It depends we need build slash main.o and build slash game.o those object files and then no forget to link with the dash l sdl two and that would generate build slash labyrinth that we can execute in a command line with dot slash build slash labyrinth.
With that we reach the end of this lesson. Thank you very much for watching and see you in the next one.
No comments yet (loading...)
No comments yet (loading...)
Did you like the lesson? 😆👍
Consider a donation to support our work: