A Makefile Tutorial By: volcanic

(Could be part 1 of 2 if requested, see end of file)

:::PREFACE:::
Ive been learning C++ for the last few months, programming in linux and
using the g++ compiler. I have used makefiles during the course of my learning but
found them a bit hard to understand at first.
I turned to AO for some good explinations,
but couldn't come up with much of anything. Seeing that many people turn to AO to get help
with learning C++, I thought I would pass what I learned on to others in the same boat as I am in.

This Tutorial is specific only to C++ programming, I am not too sure about makefile equivalents in other
languages, I am sure the equivalents are quite simmilar, but I don't want to misinform anyone.
The basic premise of a makefile will allow it to be used for purposes other than compiling a program,
but I will leave these purposes for another conversation.
I do, however, welcome any comments that might be of use to somebody reading this tutorial.

If anything stated below is inconsistent or incorrect, please reply or email me and I will edit my tutorial.
Thank You!

:::BASICS:::
What is a makefile?
At its most basic form, a makefile is a set of instructions
that the compiler reads to turn your code into an executable file.
Most programs that you write will contain more than one source
code files (.cpp .h), so the makefile also acts as a set of instructions
for your compiler to properly build the program out of these many
source code files, this is called linking.

How do I implement a makefile?
By using the make utility(part of the *nix and/or shell command line).
A good idea when writing the source for your program is to keep each
program in it's own individual directory (seems logical enough). Then, in
that particular directory, you will create your makefile (described below).
Once you are ready to compile(make) your program, you type: make.
Make will look for the makefile and take it from there.

:::A QUICK NOTE:::
-Comments can be placed in a Makefile by placing a pound s ign in front of the line
to be commented (#).


:::WALKTHROUGH:::
** Using the basic knowledge listed above, you should be armed with enough info
COLOR=royalblue]**[/COLOR]to get you through the rest of this tutorial. Any additional comments/remarks are
COLOR=royalblue]**[/COLOR]welcome.

1. Simple Compilation:
A small program requires at least one file. When a program is
compiled, it goes through 3 stages:
Compiler: All code is converted into assembly code (.s file).
Assembler: Assembly code is converted into object code,
making the origional code into a .o code (o for object).
Linker: This stage links .o code with other .o code files used in your
program.

2. Multiple File Compilation:
Source code that is divided up into more than one file goes through the same
process as the simple compilation above, with just a few more rules.

An example of a multiple file compilation:
You have two .cpp (or .c) files that depend on the same .h file.
The first stage (compiler) will convert each dependent pair into
a .s file. So the first .cpp file will be assembled together with the .h
file, creating a .s file. And the same happens with the other .cpp file.
This leaves you with two .s files.
The second stage (assembler) converts both of these .s files into .o
files. Now you have two object files.
The final stage links these two object files into one executable program,
also called a run time image.

3. Dependency:
You can see from step 2 above that the final executable program was dependent
on the two .o files made from the previous stage. You can also see that the two .o
files were dependent on the 2 .cpp(or .c) and the one .h file. This concept is strangely
enough called "dependency" and is one of the fundamental ideas behind the makefile.

When calling the make command, make will check to see what needs to be done when,
and in what order. For this, it uses each files timestamp. A timestamp is a record of the
last time a file was modified.

If you have the same program that we compiled and made into an executable above,
and you go back and change something in one of the files, then your program will need
to be re-compiled.

Of course, you only changed one of the two .cpp (or .c) files, so make will compare the timestamps
of each dependent file and will only re-compile those files who's timestamps are for a later time than
their dependent's.

4. Cleaning:
Once you run make, provided your program compiles properly, you will have some new files
appear in your directory; namely, the .o files and executable program you created.

In order to go back and make a change to your source and change the executable program, you will
need to run make again, but, make won't think it has anything to do if the .o files and the executable
file it needs to create are sitting right there in the directory.
So the files will need to be removed.
You can do this in two ways.
1. Remove them manually (using the rm filename command).
2. Create something in your make file that will remove them for you.
This "cleaner" will be shown in code a little later in the tutorial.

:::THATS GREAT AND ALL
BUT LET ME SEE SOME CODE:::
***Important Format*** The dependency code explained below follows this format:
target: source files(s)
^^^^^^command(s)
^^^^^^Replace ^'s with 1 tab character. This MUST be a tab character, no exeptions.

***Compiler Note*** For the compiler commands below, I will use the command g++
This can be replaced with the compier of your choice.

*** Using the -c option in your compile command will cause only an object (.o) file to be
produced. The last stage (linker) will not take place.

*** When #including a .h file into a program, the .h file need not be in the makefile.
But note, when the .h file changes, the rest of the program will not automatically be remade.

1. Hierarchy
When creating the code for your makefile, the format follows a hierarchial fashion,
with the most important thing on top, the executable (run image)

2. Lets Create An Example Program:
I am going to explain this using the same program from #3 above,
but let me use some names to make this easier.
(2) .cpp files are called: me.cpp & you.cpp
(1) .h file is called: him.h
**Remember both me.cpp and you.cpp are dependent on him.h
Our program is going to be called: projectUS.

3. How to write this:
First I will show the code, then I will explain it.

Code:

projectUS: me.o you.o
g++ me.o you.o -o projectUS
me.o: me.cpp him.h
g++ -c me.cpp
you.o: you.cpp him.h
g++ -c you.cpp

clean:
rm *.o projectUS


Explanation:

The first line tells us that there will be a run image that will be called projectUS, and
that it is dependent on me.o and you.o If the timestamps for the Rule Line (indented
line two) are for a later time than those for the Dependent line (line one), then the
commands for the Rule Line are followed. This rule line tells the dependent line how to
get me.o and you .o. Notice that the g++ command is flagged with the -o flag, not the -c?
This is because in this stage, the compiler will be linking objects, not compiling code into
assembly language.

So that second line will call both the third and fith lines to be run. Same thing here.
me.o is dependent on me.cpp and him.h
Timestamps are checked, and if needed, g++ -c me.cpp will be ran.
Ditto for you.o

Provided the code compiles with no errors, you will have an executable file in your directory.

The clean line allows you to call the command: make clean
This will run the command for clean listend in its rule line, namely removing all files in the directory
that end witn *.o projectUS

This brings up another good point. You can have one makefile that runs multiple programs.
For the makefile above, you can either run: make clean, or make projectUS
If you had another programs makefile in this file you could call: make otherProgramsName
Where otherProgramsName, clean, and projectUS are called a makefiles target.



:::LAST THOUGHTS/NOTES/REMINDERS:::
-The make command will be able to find a makefile whether or not it is
spelled with a capital M or a lowercase m. If you do not wish to use either
of these, you can use the command: make -f whatevernameyouwant

-In the case of having many files being made together, at least one of them must have
a main() function.

-No two functions can be named the same.

-There are other nice little things that the make utility allows you to do, such as set macros
and use pre-existing macros to make your makefiles easier to write and understand.
If the audience would like, I can write a part 2 to this tutorial at a later date.


Thank You For Reading

Your Friend,
Volcanic