Central Iowa Railroad Herald

CIRR.COM

Compile Time

Writing Portable Makefiles


Hey, Good looking

So, what should a good Makefile look like? Here are some samples from a current project. The project uses the System V make, and it's associated include mechanism. This isn't an issue in this case, as it's for a single customer who is unlikely to ever port this software off of System V (Solaris.) The project is being developed using Informix's 4GL database programming language.

The Makefiles are of two types. Listing 1 and Listing 2 are the common include files for all programs. These include build rules for the software (not covered by the default rules in make), and the build rules for objects common to most of the directories.

Common/rules.mk:

#---------------------------------------------------------------------------
# @(#) $ Id: rules.mk,v 1.12 2002/06/21 20:43:51 eric Exp $
#
# Common Makefile definitions needed to build the private billing
# system
#---------------------------------------------------------------------------

INSTALL_ROOT=${DESTDIR}/opt/privbill
BINDIR=${INSTALL_ROOT}/bin
SCREENDIR=${INSTALL_ROOT}/screens

# where to find interesting Informix bits
INFORMIXDIR=/opt/informix
INFORMIXLIB=$(INFORMIXDIR)/lib:$(INFORMIXDIR)/lib/tools

# needed to get c4gl to compile
INFORMIXSERVER=tahoe
DBPATH=/home/privbill

# set -fwritable-strings when using gcc, as
# 4gl likes to scribble into constant strings
CFLAGS= -I${INFORMIXDIR}/incl/tools -g -fwritable-strings
C4GLFLAGS= -anyerr -fwritable-strings -g

LDOPTS=-R$(INFORMIXLIB)

#
# 4gl "compilers" (with default arguments)
#
C4GL=c4gl
FORM4GL=form4gl -q

COMMON_OBJS= common.4go globals.4go

.SUFFIXES: .4go .4ge .4gl
.SUFFIXES: .frm .per

.4gl.4go:
 ${C4GL} ${C4GLFLAGS} -c -o $@ $<

.per.frm:
 ${FORM4GL} $*

Listing 1

Listing 1, Common/rules.mk defines a number of macros needed through out the project. It also defines the rules needed to convert source files into objects, and on screen form files into their compiled editions.

Among the make variables defined are CFLAGS, to modify the C compilation environment; LDOPTS to include extra options to the linker (in this case, adding the Informix library directory to the run time loaders search path); C4GLFLAGS to modify the compilation environment for the C4GL compiler; and C4GL to define the compiler.

make's list of known suffixes is also augmented to include the suffixes used by Informix's c4gl source compiler, and Informix's form4gl form compiler.

This Makefile ends with suffix rules to compile 4GL source code into objects, and 4GL form source into their compiled format.

Common/sources.mk:

#---------------------------------------------------------------------------
# @(#) $ Id: sources.mk,v 1.8 2002/06/24 19:02:06 eric Exp $
# $ Source: /home/cvsrep/PrivBill/Common/sources.mk,v $
#
# Commonly used objects
#---------------------------------------------------------------------------

globals.4go: ../Common/globals.4gl
 ${C4GL} ${C4GLFLAGS} -c -o globals.4go ../Common/globals.4gl

common.4go: ../Common/common.4gl ../Common/globals.4gl
 ${C4GL} ${C4GLFLAGS} -c -o common.4go ../Common/common.4gl

version.4go: version.4gl

VSRCS=$(4GLSRCS) ../Common/common.4gl

version.4gl: $(VSRCS) ../Common/sources.mk
 @echo "Generating version.4gl"
 @rm -f version.4gl
 @# long script to auto generate version.4gl deleted >version.4gl

usps.o: ../Common/usps.c
 ${CC} ${CFLAGS} -c -o usps.o ../Common/usps.c

${SCREENDIR}:
 mkdir -p ${SCREENDIR}

${BINDIR}:
 mkdir -p ${BINDIR}

Listing 2

Listing 2 defines the targets for the object files and source files that are included from a number of other program directories. In an ideal world, three of these object files, globals.4go, common.4go, and usps.o would be built into a library to be linked with the programs in other directories. This project isn't yet in the ideal world. :-)

A target is included to automatically generate a version information function for every program that desires one. The in-line script, which was removed for brevity, is about 60 lines long, and generates a source file of valid code. The generated version.4gl source is then compiled using an earlier dependency. The resulting file is against the programs objects, and is made available via an option in the program's menu.

Don't be afraid to include scripts in-line in the Makefile, but do make them readable. In-lining the script in the Makefile reduces the number of source files that have to be provided as part of the distribution, hopefully keeping things simpler.

CSR/Makefile

#---------------------------------------------------------------------------
# @(#) $ Id: Makefile,v 1.5 2002/06/20 16:48:28 eric Exp $
#
# Private Billing CSR user interface
#---------------------------------------------------------------------------

include ../Common/rules.mk

CSR_PERS= acct002.per acct003.per csrp001.per call001.per \
 qacct.per query01.per usps001.per

CSR_FRMS=$(CSR_PERS:.per=.frm)

CSR02_4GE= csr02.4ge

all: $(CSR02_4GE) $(CSR_FRMS)

install: $(BINDIR) $(SCREENDIR) $(CSR02_4GE) $(CSR_FRMS)
 for i in $(CSR_4GES); do \
  cp $$i $(BINDIR) ; \
 done
 for i in $(CSR_FRMS) ; do \
  cp $$i $(SCREENDIR) ; \
 done

CSR02_SRCS=csr_addcl.4gl csr_main.4gl csr_query.4gl csr_updcl.4gl
CSR02_OBJS=$(CSR02_SRCS:.4gl=.4go) $(COMMON_OBJS) usps.o version.4go

$(CSR02_4GE): $(CSR02_OBJS)
 ${C4GL} -o $(CSR02_4GE) $(CSR02_OBJS) $(LDOPTS) -lnls

clean:
 rm -f $(CSR_4GES)
 rm -f $(CSR02_OBJS)
 rm -f $(CSR02_SRCS:.4gl=.4ec)
 rm -f $(CSR02_SRCS:.4gl=.ec)
 rm -f $(CSR02_SRCS:.4gl=.c)
 rm -f $(CSR_FRMS)

$(CSR02_OBJS): ../Common/globals.4gl

4GLSRCS=$(CSR02_SRCS)

# common sources/objects (../Common/globals.4gl, ../Common/common.4gl)
include ../Common/sources.mk

Listing 3

The Makefile above builds the program csr02.

The file starts off with the projects standard header, which includes some edit history.

Then a number of macros are defined, allowing the rest of the Makefile to be clearer, as well as reducing the possibility of mistakes in references, etc.

The first real target defines what should happen if a bare make is typed in the program directory. After that, the install target is defined. The install target can really be defined almost anywhere, the author finds it useful to define it early.

The next real target actually links the objects into the executable. All of the objects are build using the rules defined by ../Common/rules.mk. A clean target is defined, to remove the executables, and the intermediate objects, and other cruft left around by the build process.

Finally, all the objects are explicitly declared as having a dependency on .../Common/globals.4gl (which contains a group of global type definitions and the like.)

Hopefully, this gives you a good idea of what a readable Makefile should look like, and what targets should be included.


If you have any questions about our site, please send us mail.
Copyright 2000,2001 Central Iowa (Model) Railroad Contact Us Referral
Program
Support
$Id: 2002-10.html,v 1.1 2002/08/21 17:07:55 cirr Exp $ Terms of Service Privacy Information