Understanding Compilation Flags and Makefiles

This document may be copied and distributed provided that George Peter Staplin is given credit.

Last updated Aug 31, 2005

This tutorial consists of an explanation of using common flags with GCC to fix software that does not compile, examples to help you compile your own programs, and information about creating a simple makefile.

The most common type of errors when compiling software from source code are caused by the include and library paths not being correct. The authors system may have include files and libraries in different places, and/or symbolic links to include files or libraries.

Compilation Flags

In this example a source code file named x_draw.c will be compiled that uses the X Window System.

The example below lists errors about missing X11 header/include files, despite the fact that they exist on the system. The problem is that the compiler does not know the path to the header/include files.

$ gcc x_draw.c 
x_draw.c:5: X11/Xlib.h: No such file or directory
x_draw.c:6: X11/Xutil.h: No such file or directory
x_draw.c:7: X11/keysym.h: No such file or directory
x_draw.c:8: X11/keysymdef.h: No such file or directory
x_draw.c:9: jpeglib.h: No such file or directory

To tell the compiler the path to the X11 include files -I/usr/X11R6/include is used.

$ gcc x_draw.c -I/usr/X11R6/include 
x_draw.c:9: jpeglib.h: No such file or directory

To fix the last include file error message the compiler needs to know the path to the jpeglib.h header file. On this system it is located in /usr/local/include.

$ gcc x_draw.c -I/usr/X11R6/include -I/usr/local/include

The compiler will now try to link the object file into an executable. An object file ends with an .o extension. On this system when the linking begins many errors appear.

/var/tmp/ccH14605.o: Undefined symbol `_jpeg_std_error' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_stdio_src' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_read_header' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_start_decompress' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_destroy_decompress' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_read_scanlines' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_finish_decompress' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_jpeg_destroy_decompress' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_XFreeGC' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_XDestroyWindow' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_XFlush' referenced from text segment
/var/tmp/ccH14605.o: Undefined symbol `_XCloseDisplay' referenced from text segment

The beginning errors are jpeg functions. To solve the JPEG errors the linker needs to be told which libraries to link with. On this system the path to the JPEG library is /usr/local/lib/libjpeg.a. There are two ways to tell the linker the path to the JPEG library, which are demonstrated in the examples below.

Example 1
$ gcc x_draw.c -I/usr/X11R6/include -I/usr/local/include /usr/local/lib/libjpeg.a  
Example 2
$ gcc x_draw.c -I/usr/X11R6/include -I/usr/local/include -L/usr/local/lib -ljpeg

The first example specifies the full path to the library. The second example tells the linker to look in the /usr/local/lib directory and to use the jpeg library. The linker option -l instructs the linker to use a library that has the string jpeg in it. Sometimes the linker will be confused by the available libraries in a directory if -l is used, in such cases specifying the full path is generally a good idea.

When compiling with the jpeg flag the linker is listing errors about undefined X symbols.

$ gcc x_draw.c -I/usr/X11R6/include -I/usr/local/include -L/usr/local/lib -ljpeg      
/var/tmp/ccl31739.o: Undefined symbol `_XOpenDisplay' referenced from text segment
/var/tmp/ccl31739.o: Undefined symbol `_XCreateWindow' referenced from text segment
/var/tmp/ccl31739.o: Undefined symbol `_XClearWindow' referenced from text segment
/var/tmp/ccl31739.o: Undefined symbol `_XMapRaised' referenced from text segment

The solution is to instruct the compiler where the X libraries are and which library to link with. In this case the linker should link with libX11.a in /usr/X11R6/lib or the shared object (*.so) version of that library.

If you are uncertain which library contains the symbol for a particular function, you can use the nm program to find out what symbols a library or executable provides. Some programs may not have symbol information that nm can read. Symbol information can be removed from an executable using the strip program. Some versions of nm support a -u flag that may be useful to determine the dependencies of a pre-built executable.

$ nm libX11.a | less
Context.o:
00000000 t _ResizeTable
00000268 T _XDeleteContext
00000218 T _XFindContext
00000120 T _XSaveContext
000000b0 t __XFreeContextDB
00000000 t ___gnu_compiled_c
         U _calloc
         U _free
         U _malloc
00000000 t gcc2_compiled.

By now the complete compile command looks something like this:

$ gcc x_draw.c -I/usr/X11R6/include -I/usr/local/include -L/usr/local/lib -ljpeg  -L/usr/X11R6/lib -lX11           

The program has successfully compiled and linked. The directory should now contain a file named a.out. This file can be executed at a shell prompt using ./a.out. You can specify the name of the output executable in your command with the -o flag.

$ gcc somefile.c -o my_executable

It's often useful to know what libraries an executable or library depend on. For this purpose the ldd (list dynamic dependencies) program is provided.

$ ldd x_draw

x_draw:
        -lX11.6 => /usr/X11R6/lib/libX11.so.6.1 (0x4001c000)
        -ljpeg.62 => /usr/local/lib/libjpeg.so.62.0 (0x400b2000)
        -lc.23 => /usr/lib/libc.so.23.1 (0x400cf000)

Makefiles

Make is a tool for the automation of commands. Instead of typing out long commands repeatedly, one command is issued, such as make, or make target. The makefile contains information about the commands to issue, what the commands will create, and what they depend on. Make saves a programmer time by running a target's commands only if a file has been modified, or if it doesn't exist.

The example below is a simple makefile that will be explained. (For example purposes 2 targets are used to generate x_draw. It's possible to combine these 2 targets.)
  
all: x_draw.o x_draw 

x_draw.o: x_draw.c
	gcc x_draw.c -c -I/usr/local/include -I/usr/X11R6/include 

x_draw: x_draw.o
        gcc x_draw.o -o x_draw -L/usr/X11R6/lib -lX11 -L/usr/local/lib -ljpeg

clean: 
	rm x_draw.o x_draw

The top line of the example with all: is the target that is invoked when make is run without any arguments. In other words; it's the default target.

The character ":" marks the beginning of the target's dependencies. The first dependency in the all target is x_draw.o. Make will search for a target or file named x_draw.o. In this makefile it finds the target. The target is x_draw.o. The target x_draw.o depends on x_draw.c.

At this point make doesn't know if it should run the command associated with the x_draw.o target. The first step in the decision to run the command is to test if x_draw.o does not exist. If x_draw.o does not exist then the command is run to create it. If x_draw.o does exist then the modification times of x_draw.o are compared to x_draw.c. If x_draw.c is newer than the x_draw.o, then the command is run.

A target's command is run if the target file does not exist, or if a dependency is newer.

Notice that the command to compile x_draw.c to an object file is indented. A tab indent before a command is required.

The gcc command uses the -c flag which causes gcc to not invoke the linker, so it compiles an object (x_draw.o) file, and not an executable.

Makefiles can use variables to save the programmer from retyping common strings. Suppose for example that you wanted to have a variable that stores the name of the output executable.

PROGRAM=my_xdraw
The link target can now use the PROGRAM variable as the output executable name.
x_draw: x_draw.o
	gcc x_draw.o -o $(PROGRAM) -L/usr/X11R6/lib -lX11 -L/usr/local/lib -ljpeg

Information can be appended to a variable that holds a list using +=. This can be useful because it saves a programmer time, by allowing the placement of += anywhere after the initial definition. All commands after the += will use the new information that was added. Make provides many other useful operators that may be useful as you progress. At this point manuals should be useful to you.

For further information see your system documentation or these excellent manuals:


Keep it Simple