Novell Home

How-To GNU Tools

Novell Cool Solutions: Feature
By Simon Nattrass

Digg This - Slashdot This

Posted: 17 Aug 2004
 

Abstract
The GNU tools from the Free Software Foundation comprise of over 3000 packages and regardless of which Linux distribution used, the GNU tools are omnipresent. In fact GNU is actually Linux!, or more precisely, what we call ?Linux? should be labeled ?GNU/Linux? since the Linux is simply the kernel and the GNU tools which complement the kernel provide the complete system.

This document concentrates on those tools used for software development, explaining the rationale for each, and providing a brief introduction to their use. The GNU developer tools are a powerful and popular suite and as such there is a wealth of detailed information available on their use. This document is not an attempt to replace or repeat this information, instead it serves as a synopsis for the developer moving to the Linux (GNU/Linux !) platform and to this new developer tool set.

Contents

1. The Complier - gcc


2. The Build Tool -- make


3. Autotools -- autoconf, automake and libtool


4. Version Control -- cvs


5. Debugging - gdb
The Complier - gcc

The term ?gcc? applies to two things. The official interpretation - GCC is the GNU Compiler Collection and refers to the suite of compilers with support for ANSI C (gcc), C++ (g++), Objective C, Java (GCJ), Fortran, and Ada (GNAT), in addition to providing libraries for these languages -- libstdc++, libgj, etc. Historically, GCC was the "GNU C Compiler" and hence the name of the ANSI C Compiler front-end -- gcc.

Throughout this paper, the familiar HelloWorld! (hello.c) program will be used as a simple example, putting each tool in context and showing its use. If you haven't seen it before, here is HelloWorld! In all its glory:

#include 

int main()
{
     printf(?Hello World!\n?);
}

Using gcc to compile HelloWorld is as simple as:

$ gcc -o hello hello.c
$ ./hello
Hello World
$

Here the -o flag indicates the name of the object code file. Without this, the file would be named a.out. The gcc compiler has a multitude of flags available to it, as do the other compilers in the GCC. For more details see the official GCC documentation.

Commons Compilation Options

A cursory glance at the gcc documentation reveals a profusion of compiler options and the examples below list a few of the more commonly used ones.

  • Include head files outside the standard /usr/include

          $ gcc -I/usr/openwin/include -o hello hello.c


  • Link against a specific library. Here the 'm' in '-lm' is shorthand for libm.a which is the math library in /usr/lib

          $ gcc -lm -o hello hello.c


  • Generate symbolic link information for debugging.

          $ gcc -g -o hello hello.c


  • Generate all warning messages concerning any questionable code.

          $ gcc -Wall -o hello hello.c


  • Optimize code. This is equivalent to -O1 or optimization level one. Further optimization is possible with -O2 and -O3 flags, but leads to longer compilation time. It is not advisable to mix optimization with generating debugging information.

          $ gcc -O -o hello hello.c
The Build tool - make

For the uninitiated, make is used to automate the compilation of multiple files, dealing with their dependencies on other software, libraries, etc. Make was originally created by Dr. Stuart I. Feldman in 1977, since which time several improved derivations have been created, one of which is GNU make.

Make works via the parsing of a makefile, which details a set of dependencies and rules. Each dependency has a target, usually a file to be created and a set of source files on which the target is dependent. Each rule describes how to create the target from the files on which it is dependent.

      foo.o: foo.c foo.h bar.h                 (dependency)
                  gcc -o foo.o foo.c                   (rule)

Here that target foo.o depends on (noted by the colon) foo.c, foo.h and bar.h. The rule describes how to create the target from the dependent files.

Below is a simple Makefile for the HelloWorld! program mentioned previously. The source code has been placed in src/ while the object code will now reside in bin/:

Makefile

hello: src/hello.c
     gcc -o bin/hello src/hello.c

The indentation for the rule is required and must be a tab (a space will not work!). This causes a problem since several spaces appear much like a tab, in addition to which, spaces at the end of lines can cause the makefile to fail!

Autotools - autoconf, automake, and libtool

Where the other tools might be familiar to you, or at least you may have used an equivalent on another platform, the GNU Autotools (also known as the GNU build system) are somewhat different. The tools: autoconf, automake and libtool, work together to create a package which is portable and uniformly installable among UNIX-like platforms.

Packages built with the GNU build system are installed from source via the commands:

$ ./configure; make; make install

Autoconf
The first of the suite to be developed, Autoconf tests the features of the host system at build time to determine which are supported, producing a configure script which in partnership with the file Makefile.in, produces a GNU-compliant Makefile to build the project. This technique enables source code packages to be installed across the differing UNIX-like systems using the same code base.

Automake
Prior to Automake, developers were still required to write the Makefile.in consumed by the configure script which details the steps required to build the project. Since many of the steps are common across the UNIX platforms, the creation of Makefile.in was subsequently automated via Automake. Automake generates the complicated Makefile.in from a much simpler Makefile.am, for which the developer is responsible.

Libtool
Libtool produces portable software libraries, taking care of the creating, linking and loading of static and shared libraries across many platforms, encapsulating the platform-specific dependencies, and the user interface, in a single script. When a user builds a project on a platform, libtool organizes the compiler and linker with the appropriate switches and environment to build the correct libraries accordingly.

Since Autoconf and Automake are more frequently used than lib tool, this paper concentrates on these two tools.

The relationship between Autoconf and Automake can be represented as follows:

An Example:

A developer wishes to distribute an application (HelloWorld!) across all the UNIX-Like platforms, so that users wishing to install it may do so via the usual sequence:

$ ./configure; make; make install

Such deployment is usual, since packages built using the GNU build system are common, and this process of running configure and then the makefile targets familiar to most Linux users.

This small project requires the developer to provide only two files. Makefile.am and configure.ac (also named configure.in in older versions of autoconf) need to be created.

Makefile.am

bin_PROGRAMS = hello
hello_SOURCES = src/hello.c

Makefile.am is a high level specification of what needs to be built and where to install the object code. It is essentially a series of make variable definitions.

This example defines a program to be built (hello), which will be placed in the bin directory when the install target is run. The only source file for this project is hello.c, although others could have been specified using space as a delimiter:

hello_SOURCES = src/hello.c foo.c bar.c

configure.ac

AC_INIT(hello, 1.0)
AC_CONFIG_SRCDIR(src/hello.c)
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_OUTPUT(Makefile)

configure.ac contains macro invocations and shell code fragments that are used by autoconf to produce the configure script. autoconf copies the contents of configure.ac to configure, expanding macros as they occur in the input. Other text is copied exactly.

Every configure.ac must contain a call to AC_INIT at the beginning and a call to AC_OUTPUT at the end.

  • AC_INIT processes any command-line arguments and performs various initializations and verifications. In this example, the name of the package is set to ?hello? and the version is set at 1.0.


  • AC_OUTPUT generates the Makefile in addition to any other files resulting from configuration.

After setting package name and version number, the source files path is set with AC_CONFIG_SRCDIR and then AM_INIT_AUTOMAKE runs macros required for the proper operation of the generated Makefiles. Note, older scripts may replace AC_CONFIG_SRCDIR with:

AC_INIT(src/hello.c)
AM_INIT_AUTOMAKE(hello, 1.0)

Having located the source, the script then attempts to determine which C compiler to use via AC_PROG_CC (usually cc or gcc).

With the two required files created, autoconf can now be run against configure.in. Before this step, it is necessary to gather all the macro definitions which autoconf will use (including those it is not aware of such as AM_INIT_AUTOMAKE). This is done via the aclocal script which generates aclocal.m4. Once complete, autoconf may be run and the configure script is generated, after which automake can be executed.

$ aclocal
$ autoconf

When distributing a package created using the GNU build system, it is normal etiquette to have some useful text files contained within the distribution:

  • INSTALL -- installation instructions.


  • NEWS -- lists changes which the user should be aware of, with the most recent at the top.


  • README -- general information on the package. This should be the first file to be examined (even before installation).


  • COPYING -- contains licensing information, typically the GNU General Public License.


  • AUTHORS - Lists the names, and usually email addresses, of individuals who worked on the package.


  • ChangeLog - records the changes that are made to a package in a well defined format.

Some of these files (INSTALL and COPYING) are relatively static and templates can be copied from the Automake installation into the current directory via specifying the --add-missing option to automake. The remainder are created from scratch and automake will complain if they are not found. To create empty placeholder files for future population, the touch command may be used.

$ touch NEWS README AUTHORS ChangeLog

Then Automake will run without complaint:

$ automake ---add-missing 

Once complete, the current directory will be populated similar to the following:

aclocal.m4	ChangeLog		decomp		Makefile.in	NEWS
AUTHORS		configure		INSTALL		README		src
autom4te.cache	configure.ac	install-sh	missing		bin
mkinstalldirs	COPYING		Makefile.am	

Now configure can be run to generate Makefile, and then the install target is executed.

$ ./configure --prefix=/home/snattrass/HelloWorld/
$ make install

The --prefix switch specifies the destination directory for the install target. For the HelloWorld! this is the local bin/ directory contained within the project directory (or more precisely /home/snattrass/HelloWorld/bin/). Without this switch, installation defaults to /usr/local/bin/.

While the use of the autotools is most certainly overkill for our little HelloWorld! package, for larger, more complex applications it supports users by providing a familiar install procedure and supports developers by simplifying the build and distribution procedure across the UNIX-like platforms.

Version Control - cvs

CVS is a version control system. Using it, you can record the history of your source files.

CVS is configured to manage one or more repositories, and each repository holds a collection of modules. A module is a set of files and directories which are under version control.

Normally, you never access any of the files in the repository directly. Instead, you use CVS commands to get (checkout) your own copy of the files into a working directory, and then work on that copy. When you've finished a set of changes, you check (or commit) them back into the repository. The repository then contains the changes which you have made, as well as recording exactly what you changed, when you changed it, and other such information.

CVS software consists of two components -- the server piece which manages the repositories on a server machine, and the client piece which is responsible for the synchronization between the files in the (remote) repository and those in the working directory.

CVSROOT

As CVS adheres to the client/server paradigm, it provides communication over several protocols, each with their own benefits (and drawbacks). Popular protocols are: Local, External (or Secure Shell or SSH), Password Server, GSS-API server and Kerberos Server.

In order for the CVS client to communicate with the server machine, it needs to know the following details:

  • protocol
  • server
  • repository directory
  • user name
  • module name

The concatenation of the first four items is known as the CVSROOT.

CVSROOT=:<protocol>:<user name>@<server>:<repository directory>

The exception to this rule is when the repository is on the local machine, then the user name and server are omitted and optionally the protocol (although it may be set to :local:).

CVSROOT=:local:<repository directory>

Every time CVS executes a command it needs to know the location of the repository (the CVSROOT). This can be specified on every occasion via the -d flag which will become very tedious very fast:

$ cvs -d <repository location> <command>

Or the CVSROOT can be exported once for the shell which CVS then uses:

$ export CVSROOT=:<protocol>:<user name>@<server>:<repository directory>
$ cvs <command>

A typical setting for a repository location is /usr/local/cvs/, so this will be the default for the HelloWorld! example, in addition to which the repository is held on the local box.

$ export CVSROOT=:local:/usr/local/cvs

Creating a Repository

After creating the directory to represent the repository, the repository needs to be created or initialized. This actually involves (among other things) creating a CVSROOT directory within the repository (not to be confused with the CVSROOT environment variable!). Not that the user does this manually -- instead the init command is issued to create the repository, setting up the CVSROOT directory and associated contents.

$ mkdir /usr/local/cvs
$ cd /usr/local/cvs
$ cvs init
$ ls
CVSROOT
$

Importing a Module

With the repository created, the HelloWorld! project needs importing so CVS can track the changes. This is done via the import command from the root of the project directory containing the code (/home/snattrass/HelloWorld/).

cvs import -m ?message? <module name> <vendor tag> <release tag>

The module name identifies the module within the repository, while vendor tag and release tag are bookkeeping arguments for cvs.

$ cd /home/snattrass/HelloWorld
$ cvs import -m ?initial import? hello Novell start

If all goes well, the message ?No conflicts created by this import? will eventually appear.

Checking Out a Working Copy

Once a module is present within the repository, a module may be checked out as a ?working copy? for future development via the checkout command.

$ mkdir temp_workspace
$ cd temp_workspace
$ cvs checkout hello

Committing Changes

Any changes that have been made may then be checked back into the repository via the commit command. If a file name is specified, then only changes to that file are committed, otherwise all changes to the current module are sent.

$ cvs commit hello.c

If all goes well, information on the revision numbers is output:

"new revision: 1.2; previous revision: 1.1?

The previous example details a local CVS repository. Typically the repository will be held remotely to the workspace and further steps for connection and authorization between the client and server are required according to whichever communication protocol is used. For more information on this and other details relating to CVS, read the official manual by Per Cederqvist, et al.

Debugging - gdb

Debugging tools enable the developer to examine the inner workings of a program as it executes, controlling the program flow, and inspecting variables and areas of memory, all in an attempt to understand and eliminate bugs from the code.

The GNU Debugger, GDB supports C, C++, Objective-C, Fortran, Java, assembly, and Modula-2.

To support debugging, code must be compiled with debugging turned on via the -g switch. We will use the HelloWorld! example one final time, but before we can get any real benefit from this example, the code needs to be developed a little more. After all, as it is there is little to debug!

hello.c (advanced!)

#include <stdio.h>

int main()
{
	printf(?Hello World!\n?);,

	int counter = 0;
	while (counter >= 0)
	{
		counter++;
	}
}

We have added a looping construct to the program which simply implements a counter. As it stands, the program will loop infinitely since the counter will never be negative. But perhaps we can alter this behavior through debugging.

Now compile with debugging turned on, and then launch gdb against the new object code.

$ gcc -o hello -g hello.c
$ gdb hello
.
.
(gdb)

To start debugging, run the program via the run command and then stop by sending the Interrupt Signal (Ctrl-C).

(gdb)run
HelloWorld !
Program received signal SIGINT, Interrupt.
0x08048367 in main() at hello.c:8
8		while (counter >= 0)
(gdb) 

With the program now stopped, we can step through the code line by line with the next command.

(gdb) next
10		counter++;
(gdb) next
8		while (counter >= 0)
(gdb)

To examine the value of a variable, the print is used, while set changes the value.

(gdb) print counter
 $1 = 89635867
(gdb) set counter=-2
(gdb) print counter
$1 = -2
(gdb) next
12	}
(gdb) next
0x4003ed17 in __libc_start_main () from /lib/i686/libc.so.6
(gbd) next
Single stepping until exit from function __lib_start_main,
which has no line number information.

Program exited with code 0164.
(gdb)

In addition to the commands mentioned so far, the GNU debugger supports the creation of breakpoints at set lines in the code, conditional breakpoints (watchpoints) for expressions, examination of core dumps, etc.

Summary

This paper has given a seminal examination of the most popular GNU development tools.

References


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell