Chapter 6. Compilation through gcc and make

Table of Contents
To compile a single file
Two source files: compile and build
Built-in rules
Simple program: firstprog

To compile a single file

Let's start with the simplest possible program; a version of the famous "Hello world".

#include <stdio.h>

int main()
{
  printf("Hello world.\n");
  exit (0);
}

To keep things clean, make a directory where you can put the program. Goto that directory and copy the file from /Home/guests/olsson/linux-guide/src:

$ mkdir hello
$ cd hello
$ cp /Home/guests/olsson/linux-guide/src/hello/hello.c .
Again, note the period "." at the end of the line which stands for the current directory.

To compile the program:

$ gcc hello.c -o hello

If everything is correct gcc, "the GNU project C and C++ compiler", will silently produce the executable file hello.

$ ls -l
total 12
-rwxrwxr-x    1 olsson   olsson       4919 Aug 27 16:49 hello*
-rw-rw-r--    1 olsson   olsson        106 Aug 13 13:05 hello.c
Note that the file hello is marked with "x" and thus is an executable file. The "*" following the file name has the same meaning. (It appears since ls is aliased to ls -F, and the effect of this option on the ls command is to append indicators to the names for certain file types.)

To run the command write

$ ./hello
Hello world.

This kind of manual compilation is OK for a small and simple program, but quickly becomes impracticable in larger projects. The solution is the make utility which normally relies on both some built-in rules and a Makefile that specifies how the different files depend on one another.

Already in our simple example we can use make without any Makefile:

$ rm hello
rm: remove regular file `hello'? y
$ make hello
cc     hello.c   -o hello


Two source files: compile and build

In any more serious programming the input is normally split into several files. It is then necessary with a two-step approach:

Create a directory, cd to that directory and copy the files from /Home/guests/olsson/linux-guide/src/args:

$ cd
$ mkdir args
$ cd args
$ cp /Home/guests/olsson/linux-guide/src/args/* .

Your directory now contains the two source files args.c and pargv.c, the header file pargs.h together with the Makefile that contains instructions to the make utility. Do cat args.c and cat pargv.c or use emacs to look at the source files. Try to understand the code. (In the present case the splitting of the program into two files is very artificial and only serves a pedagogical purpose.) Note the use of the header file pargv.h which exists to ensure that the two separately compilated source files do agree about the arguments and return value of the function pargv.

The Makefile is listed below (the "#" marks the beginning of a comment):

# Link the two object files into an executable file "args".
args: args.o pargv.o
	gcc args.o pargv.o -o args

# Compile source file "args.c" to make an object file "args.o".
args.o: args.c pargv.h
	gcc -c -o args.o args.c

# Compile source file "pargv.c" to make an object file "pargv.o".
pargv.o: pargv.c pargv.h
	gcc -c -o pargv.o pargv.c

Note the structure of Makefile. For each of the three groups of lines the first line (after the comment) describes the dependencies. The structure is

output-file : input-file(s)
Note that pargv.h is also specified as one of the input files. If make finds that output-file is more recent than all the input-file(s) is does nothing, but if (at least) one of the input files is modified more recently, make executes the command(s) on the line(s) below to create an updated version of output-file. The line(s) with commands have to start with a <TAB> character to be recognized as commands by make.

Now execute make and try to run the program with some arguments:

$ make
gcc -c -o args.o args.c
gcc -c -o pargv.o pargv.c
gcc args.o pargv.o -o args
$ ./args Computational science and engineering
0: ./args
1: Computational
2: science
3: and
4: engineering
$ ./args *
0: ./args
1: Makefile
2: args.c
3: args.o
4: pargv.c
5: pargv.h
6: pargv.o
(The second invocation, "./args *", illustrates the working of the shell to expand "*" into all the file names in the current working directory.)


Built-in rules

In the example above there is room for a considerable simplification. Recall our first example in the Section called To compile a single file where we actually could do without a Makefile. The command make hello together with the existence of a file hello.c was enough for make to construct the command cc hello.c -o hello. Similarly, specifying only the dependencies in the Makefile provides enough information to make to compile and link the program.

To demonstrate this


Simple program: firstprog

Let's write a simple program that writes out a table of function values to a file. We will need the output from that program for the exercise that follows next.

Start by going to a new directory, copy the file and start emacs:

$ cd
$ mkdir firstprog
$ cd firstprog
$ cp /Home/guests/olsson/linux-guide/src/firstprog/* .
$ emacs firstprog.c &
/* Simple program */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

main(int argc, char *argv[]) {
  double limit = 10.0;
  int i;
  FILE *out;

  out = fopen("datafile", "w");

  if(argc > 1){
    limit = atof(argv[1]);
  }

  for(i = 0; i <= 100; i++) {
    double x = 0.01 * i * limit;
    fprintf(out, "%g %g %g\n" x, sin(x), sin(x/3.0));
  }
  fclose(out);
}

This time we will try and compile from within emacs (i.e. not in the shell). Choose the Tools menu on the toolbar and select Compile. This will give a line make -k in the mini-buffer at the bottom of emacs. Write firstprog and press return. Emacs now splits its window vertically into two windows and displays the compilation in the new window.

This time the compilation gave an error:

make -k firstprog
cc     firstprog.c   -o firstprog
firstprog.c: In function `main':
firstprog.c:19: parse error before "x"
make: *** [firstprog] Error 1

Compilation exited abnormally with code 2 at Mon Sep 1 13:03:33 
To jump to the offending line type "C-x `" i.e. Ctrl-x followed by a backquote (use Shift and press the accent key twice on the swedish keyboard) goto the right position on the line and insert the missing ",". Don't forget C-x C-s to save the buffer.

Selecting Compile again gives make -k firstprog and it is now enough to press return. Now there is another error:

$ make firstprog
cc     firstprog.c   -o firstprog
/tmp/ccCZaSsy.o(.text+0x78): In function `main':
: undefined reference to `sin'
/tmp/ccCZaSsy.o(.text+0x86): In function `main':
: undefined reference to `sin'
collect2: ld returned 1 exit status
make: *** [firstprog] Error 1
The error "undefined reference to `sin'" tells us that the linker ld could not find the function sin. This function is present in a library libm Since all the standard library names start with "lib" one only specifies the "m". In the gcc command this is specified as "-lm" where "-l" tells that what follows is a library. To solve this we write a Makefile that does what we want:

firstprog: firstprog.c
	gcc firstprog.c -lm -o firstprog
If we instead want to use the built-in knowledge for construcing the gcc command we should specify the variable LOADLIBES. The Makefile that does this may look like
CFLAGS = -g -O4
LOADLIBES = -lm

firstprog: firstprog.c
Here we have also specified the compiler flags ("CFLAGS=") "-g" to include debugger information and "-O4", to enable optimization (level 4). This time it should run all through.

To run the program:

$ ./firstprog
The file that is created is to be plotted in the next chapter.