Programming Utilities Guide

Organizing a Project for Ease of Maintenance

As mentioned earlier, one good way to organize a project is to segregate each major piece into its own directory. A project broken out this way usually resides within a single file system or directory hierarchy. Header files could reside in one subdirectory, libraries in another, and programs in still another. Documentation, such as reference pages, can also be kept on hand in another subdirectory.

Suppose that a project is composed of one executable program, one library that you supply, a set of headers for the library routines, and some documentation, as in the following diagram.

Graphic

The makefiles in each subdirectory can be borrowed from examples in earlier sections, but something more is needed to manage the project as a whole. A carefully structured makefile in the root directory, the root makefile for the project, provides target entries for managing the project as a single entity.

As a project grows, the need for consistent, easy-to-use makefiles also grows. Macros and target names should have the same meanings no matter which makefile you are reading. Conditional macro definitions and compilation options for output variants should be consistent across the entire project.

Where feasible, a template approach to writing makefiles makes sense. With a template, you track how the project is built. All you have to do to add a new type of module is to make a new directory for it, copy an appropriate makefile into that directory, and edit a few lines. You also need to add the new module to the list of things to build in the root makefile.

Conventions for macro and target names, such as those used in the default makefile, should be instituted and observed throughout the project. Mnemonic names mean that although you might not remember the exact function of a target or value of a macro, you will know the type of function or value it represents by the name and that's usually valuable when deciphering a makefile also.

Using include Makefiles

One method of simplifying makefiles, while providing a consistent compilation environment, is to use the make:

	include filename 

This directive reads in the contents of a named makefile; if the named file is not present, make checks for a file by that name in /etc/default.

For instance, there is no need to duplicate the pattern-matching rule for processing troff sources in each makefile, when you can include its target entry, as shown below.

SOURCES= doc.ms spec.ms 
...
clean: $(SOURCES) 
include ../pm.rules.mk

Here, make reads in the contents of the ../pm.rules.mk file:

# pm.rules.mk 
# 
# Simple "include" makefile for pattern-matching 
# rules.  

%.tr: %.ms 
         	troff -t -ms $< > $@ 
%.nr: %.ms 
         	nroff -ms $< > $@

Installing Finished Programs and Libraries

When a program is ready to be released for outside testing or general use, you can use make to install it. Adding a new target and new macro definition to do so is not difficult:

DESTDIR= /proto/project/bin 

install: functions 
        	-mkdir $(DESTDIR) 
        	cp functions $(DESTDIR)

A similar target entry can be used for installing a library or a set of headers.