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/stud-intro/src:
$ mkdir hello $ cd hello $ cp /home/stud-intro/src/hello.c . |
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 |
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 make use of make:
$ rm hello $ make hello cc hello.c -o hello |
In any more serious programming the input is normally split into several files. It is then necessary with a two-step approach:
Each source file file1.c, file2.c is compiled separately into the object files file1.o and file2.o.
The object files are then linked to make the final executable file.
Create a directory, cd to that directory and copy the files from /home/stud-intro/src/args:
$ cd $ mkdir args $ cd args $ cp /home/stud-intro/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 to look at the source files and try to understand the code. 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. In the present case the splitting of the program into two files is very artificial and only serves a pedagogical purpose.
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) |
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 Physics and computers 0: ./args 1: Physics 2: and 3: computers $ ./args * 0: ./args 1: Makefile 2: args.c 3: args.o 4: pargv.c 5: pargv.h 6: pargv.o |
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
Delete the objects files and the executable file, rm args.o pargv.o args
Delete all the command lines in the Makefile:
Open an emacs to edit Makefile by typing "emacs Makefile &" in an xterm.
Position the cursor at the beginning of each line that should be deleted (the lines with "gcc ..."). Now C-k deletes the text to the end of line and another C-k removes the newline character as well.
Save the buffer to disk with C-x C-s
Examine the output from "cat Makefile" in the shell to make sure that the changes really were effective.
# Link the two object files into an executable file "args". args: args.o pargv.o # Compile source file "args.c" to make an object file "args.o". args.o: args.c pargv.h # Compile source file "pargv.c" to make an object file "pargv.o". pargv.o: pargv.c pargv.h |
Run make again!
$ make cc -c -o args.o args.c cc -c -o pargv.o pargv.c cc args.o pargv.o -o args |
As a last step we write this in a way that opens for a simple generalization to cases with a larger number of separate source files and also shows the mechanisms for linking to libraries, include information for the debugger in the executable file, and enable optimization.
# Example Makefile, Peter Olsson 2004-08-26 CFLAGS = -g -O4 LOADLIBES = -lm # LDFLAGS = -L/home/olsson/lib # CPPFLAGS=-DFAST OBJS = args.o pargv.o HEADERS = pargv.h args: ${OBJS} ${OBJS}: ${HEADERS} |
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 following exercise.
Start by going to a new directory, copy the file and start emacs:
$ cd $ mkdir firstprog $ cd firstprog $ cp /home/stud-intro/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 |
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/ccUe3HJt.o: In function `main': /tmp/ccUe3HJt.o(.text+0x7e): undefined reference to `sin' /tmp/ccUe3HJt.o(.text+0x98): undefined reference to `sin' collect2: ld returned 1 exit status make: *** [firstprog] Error 1 |
firstprog: firstprog.c gcc firstprog.c -lm -o firstprog |
CFLAGS = -g -O4 LOADLIBES = -lm firstprog: firstprog.c |
To run the program:
$ ./firstprog |