My programming habits
i.e. why
I
do not use make
My
programming habits formed well
before Unix.
My first (Fortran) programs were written (on a card punch) for an Univac 1100
and then for an IBM 360 machine (I used the latter also a lot via
Electric).
Therefore I got well acquainted with the classical C-L-G phases of programming
although perhaps the latter tended to be slightly blurred on the IBM VM/CMS systems,
where one usually ran a program from the relocatable via the
LOAD program (START command
For those which do not have clear ideas on it
- one edits a source file (just to recall a few filetypes :
FORTRAN on VM/CMS, &
on HP-RTE, .FOR on VAX VMS, .f on Unix)
- one compiles the source file into a relocatable object
(just to recall a few filetypes :
TEXT on VM/CMS, %
on HP-RTE, .OBJ on VAX VMS, .o on Unix)
- one links the relocatable with the libraries and generates
an executable
(just to recall a few filetypes :
MODULE on VM/CMS, no prefix
on HP-RTE, .EXE on VAX VMS, no suffix on Unix ...
... oh yes, I do deprecate a.out !)
- one runs the executable usually calling it by name (tricks might be
necessary to do that under some OS's ... but the determined programmer can always
coerce the system to do what one wants ! even on Unix one has to
chmod ugo+x and place it in the path)
Quite soon I met the need for libraries, both because I had to use libraries
written by others (for graphics, for accessing satellite data), and because I'm lazy
and tend to re-use software I've already written (I do use code I wrote more than 15
years ago, and I regularly use one routine, Bevington's CURFIT, which was
published in 1969).
Therefore, at the time I worked on HP-RTE, I devised
the following model which I still find valid and applicable :
- I write a source file for each main program
This usually contains just the code for the main itself, plus, when necessary
- the code for any subroutine which is called exclusively by that
particular main program
- the code for any library subroutine which I want to override
with a private version for that specific program
- this includes the case of a libary subroutine which I want to modify,
temporarily whileI'm working on it and testing it
- any BLOCK DATA routines which might be necessary to force getting
in a particular COMMON block and/or assigning values to it
- I collect all source files (for a particular project) in a specific
directory, the sourcedir
- I write a source file for each subroutine
This usually contains the code for a single routine, except in the case this
routine calls exclusively other service routines, in which case I merge
their code in the same source file
- All the source files of subroutines in the same library are
collected in one directory with the name of the library
- All library directories are subdirectories of a specific
directory, the libsourcedir
- The library routines are compiled into a relocatable library
(just to recall a few filetypes :
TXTLIB on VM/CMS, ?
on HP-RTE, .OLB on VAX VMS, lib.a on Unix)
- All libraries reside in a specific
directory, the libdir
- I keep the list of the libraries needed by a particular main (typically, by
a family of main programs which require the same libraries) into a one-line
ASCII file, the loader file
- I compile the main program, link it against the libraries listed in the
loader file and delete the relocatable object (which are
generated in a scratch directory, both for main and library subroutines)
- I have the executables placed automatically in a specific directory,
the targetdir, and assign them execute permission, and access them
from the path
I enforced the above model implementing it via a number of scripts, the current
incarnation of which exists for VAX VMS (which I do not use any more) and for several
flavours of Unix (Sun, Ultrix, OSF/1 aka DU aka Tru64). It is out of the scope to give
a full description of this
Real Programmer Tool
(I have computer readable documentation,
but on an offline machine ... if anybody is really interested can contact me, for
the documentation or a copy of the scripts ... but I suggest one tries to write
one's own set). Anyhow all is done with a few commands :
- a few auxiliary commands to define in the environment the various directories
(sourcedir, libsourcedir, etc.) plus one to define compiler options. Usually I
collect all these in a configure script specific for each project.
- compile program mode to compile a main program. The mode
allows to hide or show compilation errors, and to see a program listing (or emulate
it for compiler which do not support it). If I run in an X window environment, the
listing may be flagged green (no errors) or red (errors).
I actually use this command only rarely, during early development. In general I use
the following.
- comlink program loaderfile mode to compile
and link a main program, delete the object and keep the executable.
- complib library routine mode to compile a routine, insert
it in the relevant library, and delete the object. Can also recompile all
routines in the library.
- I also have a ccomplib for the rare case I have to use C routines.
With respect to the above model, I have introduced an additional detail, i.e. the
handling of include files. Since the INCLUDE statement is intrinsically
unportable because it makes reference to system dependent file names (or
actually, directory names), I use
in my programs exclusively pathless include files of the form
INCLUDE 'file.inc'
I collate all include files in a specific directory, the incdir, and let my
scripts hide all system specific arrangements to locate it and deal with it.
Given that I find myself at ease with the above arrangement, why should I use make ?
What will make give me more ? In theory, dependencies ! The possibility to recompile and relink all items affected by an update to a specific item. However they way I see
dependencies in my model is the following :
- a main program or subroutine source depends on the include files it contains
(and on nested include files)
- a relocatable library depends on the source of any subroutine in that library
- an executable depends on the source of its main, and on the source of any routine
which is actually called by that program
- I have no desire or intention to keep around redundant relocatable objects, since they are
either incorporated in libraries, or linked in executables.
However make does not use that model. If I edit subroutine suba but
not subroutine subb, it will update the library in which both are contained,
and recompile (since there are no objects around) and relink all programs which call any subroutine in that library : those who call suba,
as well as those who call subb or any other routine !
Also I found that the behaviour of make is system dependent.
A colleague of mine developed a
tool
to create and manage makefiles using my model. They incorporate a modified version
of a script of mine, derived from the Real Programmer Tool, to find dependencies.
These script relies on the use of a loader map which is not created under
all operating systems (it is under DU and it was under Ultrix).
At the time such tool (a collection of shell scripts) was created, it contained almost as
many lines of shell code, as source code lines in the
project for which it was
created !
sax.iasf-milano.inaf.it/~lucio/WWW/Opinions/forhabit.html
:: original creation 2007 lug 11 10:40:24 CEST ::
last edit 2007 Jul 11 10:40:24 CEST