Hands-on guide to GNU Make
From GLUG-BOM
You are here: Main Page >> Howtos >> Hands-on guide to GNU Make
Contents |
Overview
This how-to guide is a very quick (or as one would say, 10-minute) guide to start using GNU make. This tutorial assumes, you do know to program in C (though, any other language could be used in place of C). It is assumed, that you have not used 'make' before this and wanted a real quick guide to start using make and identify its purpose right away. This tutorial essentially supplements the GNU make manual which is ideally a great document to start with, to know more about make.
Note: The example below (and this tutorial) focuses on 'beginners' and avoids any advanced techniques (that leads to better usage of the make utility) that may be used with make. The reader is expected to gain 'head-start' in this tutorial and gain more from the GNU make manual. (Use the commands below to access the make manual)
To read the detailed document
foo@localhost~:man make
To view the brief version, outlining options
foo@localhost~:make --help | less
The purpose of GNU make and its significance
Put simply, make is a utility that will help you manage a project to enhance productivity and efficiency. In other words, it is a utility that will make your computer smart enough to do work that it should have and not repeat what it may have already performed. Now, that's more of an abstraction, rather than its technical significance.
GNU make is a utility that will decide which parts (modules) of a bigger project (i.e. say a versatile calculator allowing binary, hexadecimal and decimal calculations implemented in those specific (3) modules ) needs to be compiled (or re-compiled) based on certain parameters associated with the file(s) involved in that project, to generate the final (goal) executable (i.e. the versatile calculator). It might be easier to understand this definition based on the example below.
What you need to know
- Knowledge of a programming language (C preferably, to understand the example below)
- Using a text editor such as vim / vi / gedit / kate / <your_favourite_editor>
- Using the GCC compiler (You could use: Beginners guide to GCC, if you don't know to use GCC)
Basic steps in order to understand the usage of GNU make
Before we begin with using make, there are a few things to understand. We'll understand them as follows.
Component: makefile
The makefile is a document suggesting rules
Component: rules
A rule is defined by three other components, generally.
- target: 'Usually' the output file to be generated from the 'prerequisites' based on a 'command' (action)
- Example: program_output.out (or just program_output)
- prerequisites: The input files required for the target.
- Example: program_source0.c program_source1.c
- command: The action that specifies how to accomplish / reach / build the target from the prerequisites.
- Example: gcc -o mainprog.out mainprog.c
The above components and their usage, will be understood more easily through the example below.
Example
We are assuming a non-real example (for the sake of simplicity) to understand how make works. Our project is a way too trivial example of computing the square and cube of a number.
We have three source code files
- compute.c -- This file has the entry point main() which asks for a number from the user. It prints the square and cube of that number
- square.c -- Contains the prototype and the definition of the function that computes the square of a number
- cube.c -- Contains the prototype and the definition of the function that computes the cube of a number
Write your source code for the modules
/* compute.c */
#include<stdio.h>
int main(void){
int num;
float sqnum, cubenum;
printf("Enter an integer: ");
scanf("%d", &num);
sqnum = square(num);
cubenum = cube(num);
printf("%d ^ 2 = %.2f", num, sqnum);
printf("\n%d ^ 3 = %.2f", num, cubenum);
printf("\n");
return 0;
}
/* square.c */
float square(int);
float square(int in_num){
return (in_num*in_num);
}
/* cube.c */
float cube(int);
float cube(int in_num){
return (in_num * in_num * in_num);
}
The magic 'makefile'
A makefile, as described above, is a (text) document containing rules. These rules are defined in 'target', 'prerequisites' and 'command' format.
- Our target is to generate (build or create) the application that'll ask for a number, compute its square and cube and print it. This means, generate the executable of compute.c
- The file 'compute.c' uses (or includes) the usage of 'square.c' and 'cube.c', which basically have function definition of the functions, square(num) and cube(num). So, 'target'(compute.out) depends on prerequisites -> 'square.c', 'cube.c'
- The action to generate the output file, from compute.c is given by the a command that compiles and generates the output using the gcc compiler.
So, the general form is
target : prerequisites (tab) command
- To create a makefile, open your favourite text editor and type the following in the format described below.
Hence, the makefile for our project looks like this
compute.out : square.obj cube.obj compute.c
gcc -o compute.out compute.c
square.obj : square.c
gcc -o square.obj -c square.c
cube.obj : cube.c
gcc -o cube.obj -c cube.c
Running make
- Assuming, all above files exists in the same directory all that is needed to run make, is to type the command
make
- However, to add a little more meaning to this tutorial, we'll explore few of make utility's options.
Option 1: -f FILE
This option is used to specify make to look for a FILE, and read information from it for further execution.
- E.g. : make -f makefile
Option 2: -d
This option is used to specify the utility to print all debugging information. Using this option may cause you to see loads of information on the screen, and hence you may be required to pipe the output to less
- E.g. : make -f makefile -d | less
Option 3: --dry-run
This option is used to emulate the behaviour of make (without details) but not execute any of the commands within the make file. You should generally use this before you actually make the executable to confirm, the order of executions is correct.
- E.g. : make -f makefile --dry-run
Execute the output
Issue the following command to execute the main project.
foo@localhost~: ./compute.out
Where's the fun?
Now, all that make did was just followed the makefile, compiled and generated the executable. However, note, that make will help your computer to become a bit smarter. This is how.
- Lets say, (for the sake of it) we change the cube.c to look like this:
/* cube.c */
#include<math.h>
float cube(int);
float cube(int in_num){
return ((float) pow(in_num,3));
}
- (For backwards compatibility with GCC, make the following change to the makefile, in the first line)
compute.out : square.obj cube.obj compute.c
gcc -o compute.out compute.c -lm
- Again issue the make command and see the results for yourself! (for a change)
make -f makefile -d | less
- You'll notice, that only the cube.c was re-compiled to produce the object file. Since, compute.out depends on cube.obj, compute.out was 're-generated'. This means, make understood that cube.c was modified and needed a re-compilation, whereas square.c wasn't and square.obj could be used as it is.
More about make
This tutorial, as mentioned earlier, focuses on, only giving a 'head-start' to individuals about make. There are several advanced options along make and most of them used to make it easier for programs to be created and maintained. A lot of new development tools under Linux distros, ship along complex ways to generate the makefile automatically. However, a knowledge of make helps you to understand how things work internally.

