what makes make tick?

I don't understand something until I have done it myself. I found make to be perpetually confusing until I had made a small Makefile myself and tinkered with it. This is what I propose to walk you through in this exercise. We are not going to make a program which does anything interesting at all, instead we are interested in the capabilities of the make utility.

Make can be particularly confusing because it already "knows" about a lot of things. First, it assumes that the configuration file you want to use is called [mM]akefile (that's Makefile or makefile right?). So, if you just run make on the command-line as you will often be asked to do, in fact make is reading the file [mM]akeile and processing the first target it finds in that file. We will get to targets in the makefile shortly but first I want to show you one of make's built in knowledge base, which usually makes things which gets people confused.

Copy the following bit of code into a file named yello.c

#include <stdio.h>

int main () {
    printf("I hate these dxxx hello programs\n");
    printf("When will I really understand programming?\n");
    exit(0);
}

It is a variation on the "hello world" program. You need to include the library headers for stdio.h (standard io (input-output) .h (header-file) in order to get the printf function, which knows how to print to the standard output. We know about stdout right? this is a unix class.

Now if you were to compile this tiny c program using gcc the gnu c compiler. You would type the line gcc -o yello yello.c which tells gcc to please compile the source code file yello.c into an object code file named yello. But with make you don't even have to remember this much, try this : type make yello. You are asking make to produce a target called yello which you haven't defined anywhere. On my system I get this result: (The dollar signs are stand-ins for my shell prompt.)

$make yello
cc     yello.c   -o yello
$ 

make knows a lot about how to make c programs. In this case I gave it a target and it went about finding ways in which it could produce such a target. There is no makefile with predefined targets, but in the current working directory make found yello.c. Given a target and a file of code with the same name, make assumes that I want a binary with the same name as the target I have requested. Given a filename.c, make knows what to do. It invokes the c compiler. On my system this shows up as cc, but if I ask which cc then ls -l the cc which is being called, I find that this is a symlink to gcc.

The GNU Make Manual which you can access by typing info make calls these "Implicit Rules" and explains it this way:

Using Implicit Rules
********************

   Certain standard ways of remaking target files are used very often.
   For example, one customary way to make an object file is from a C
   source file using the C compiler, `cc'.
   
      "Implicit rules" tell `make' how to use customary techniques so that
      you do not have to specify them in detail when you want to use them.
      For example, there is an implicit rule for C compilation.  File names
      determine which implicit rules are run.  For example, C compilation
      typically takes a `.c' file and makes a `.o' file.  So `make' applies
      the implicit rule for C compilation when it sees this combination of
      file name endings.
      
Anyway, make is knowledgeable about many programming languages and can be made knowledgeable about any type of dependency structure you may have, and this is what this example is going to try to demonstrate. Now, the art of using make is in declaring your own targets, or understanding those targets that the programmer of a project you are interested in has prepared for you. There are some targets which are commonly used, such as 'all' 'install' and 'clean' each of these runs completely different commands as defined in the makefile.

all
is generally the first target in the makefile and has the commands for all the successive targets and dependencies required to fulfill the make all command.
install
is usually a bunch of shell commands and scripts which will copy the compiled binaries or libraries from their location in the source code at the end of compilation, into the system wide directories, such as /usr/bin , /usr/local/bin or /usr/lib where this binary or library could be in the paths which allow them to be called from anywhere in the system. 'install' tends to have 'all' as a dependency which makes sense because if the compilation did not succeed how could you install the perhaps non-existent binaries system-wide?
clean
performs a different job, it is usually a bunch of rm commands which remove the object files, and binaries which are left hanging around in the source code. You want to make clean to force make to recompile every bit of a project.Another of make's intelligences is that because of the way in which it knows how to keep track of dependencies, if a dependency is already met, then it knows there is no more work to do. This is brilliant for the programmer who only changes one tiny piece of the source code and does not want the entire program to be recompiled, but just the little piece he is working on.

hold on!!

Ok, I feel you are getting confused. So far we have talked about targets and dependencies these are the fundamentals of make. Make can take lists of targets and their dependencies and you in a makefile supply the rules which make needs in order to create one of these dependencies should it not exist.