home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.muug.mb.ca
/
2014.06.ftp.muug.mb.ca.tar
/
ftp.muug.mb.ca
/
pub
/
muuglines
/
source
/
softport
/
softpor1.txt
next >
Wrap
Text File
|
1993-05-08
|
6KB
|
124 lines
Writing Software for Portability, Part 1
By Gilbert Detillieux, Info West Inc.
As the computer industry continues to evolve at an ever
increasing pace, old systems and their software are becoming
obsolete more quickly. The move to open systems technology
and decreasing hardware costs make it feasible to throw out
old hardware and entirely replace it every few years. But,
what about our investment in software? The cost to produce
and maintain it is increasing, and the complexity of the
software is also increasing. To avoid having our software
become obsolete too quickly, we have to design it so it will
survive the transition to new systems. We must make it
portable.
What does software portability mean? First, software must
be machine independent, so it can easily move to different
hardware architectures and systems of different scales, from
micros to mainframes. It also should be operating system
independent, as much as possible, since this also will
change with time and with hardware changes. Software also
should be designed to be independent of the environment in
which it runs -- user interfaces and interfaces to other
programs it interacts with can change. It should also be
independent of language and country specific features, if it
will be used internationally. Finally, it should have
version independence -- it should survive changes in
hardware, operating system, and programming language version
changes, as well as changes in its own code.
The selection of the right programming language is
important. A high level language is essential to assure
machine independence, but it's important that this language
follows industry standards, such as those set by ANSI and
the ISO. Also, pick a language that will fit into corporate
standards and coding practise, and that will be available on
all your target systems. Avoid non-portable features, such
as a particular compiler's language extensions, or ill-defined
or implementation dependent features in a language.
The last thing you want is to rely on a side-effect of an
operator or other non-standard feature that will break when
compiled on another system. Also avoid standards or
language features that are too new, since these aren't yet
widely supported or may change in future standards. A good
choice for UNIX systems is ANSI C, but there might be other
choices that are reasonable for your situation.
Regardless of language, structure and modularity are
important. Use a top-down design consisting of small
independent code modules. If your language supports them,
use header files for public declarations of module
interfaces. Use these headers in modules that will need
these interfaces, as well as in the module that defines them
-- this ensures consistency in the declarations. Header
files should not result in storage allocation or code
generation -- i.e. they should only declare variable and
functions, not define them. If the system has standard
headers for declaring library routines and system calls, use
them -don't make assumptions about these by declaring them
yourself.
With a well structured program, only a few low-level modules
will be machine dependent or system dependent -- the higher
level code should all be portable. These low-level modules,
on the other hand, should be general enough to be reusable
in any application. There should be a clear line between
the high-level, application specific but portable code, and
the low-level, application independent but machine specific
code. System calls, and other non-portable constructs,
should only occur in the low-level modules. You might have
to create stubs to handle missing features on a particular
system -- these should either fake the missing feature or
return an error to the higher level modules to say this
feature isn't supported.
You also might need optional modules, that will be linked in
only if certain system-specific features will be required.
This might be the case for interfaces to other programs,
such as mailers or spoolers, for example. You can design
plug-in replacements that will be selected based on the
system configuration (such as a mailer interface to either
SMTP or UUCP, or a spooler interface to either LP or LPR).
If your language supports conditional compilation
constructs, you can use these to further isolate machine
dependencies within modules. That way, one source module
can support several systems, by simply selecting the right
parts to compile. (If your language doesn't support
conditional compilation, but you're developing on UNIX,
consider using the C preprocessor or M4 on your source files
to get that capability.) As much as possible, use pre-defined
constants and macros as switches to control
compilation of system specific features. (The C
preprocessor usually defines a few symbols for the target
operating system and architecture, such as "unix" and
"sparc".) If required, you can define your own constants
and macros, either as compilation switches, or to define
system specific quantities. Make all such definitions easy
to find and change -- don't bury them all over the place, but
rather put them in a configuration header file, so they are
localized and editing for a new configuration is kept to a
minimum. (This not only saves time, but reduces the chances
of introducing errors in the code.)
Finally, use the "make" command and a Makefile, if your
systems support it. This makes it easy to control the
compilation, and eases configuration and installation.
Configuration options, constants, and macros can all be
defined in the Makefile -- this isolates them from the code,
and allows flexibility in customizing the code without
making any changes to the modules themselves. If you want
to get fancy, you could even have a program or script that
builds a Makefile tailored to a particular configuration,
based on the system and user selected option. (Most X11
Window software now includes an Imakefile, a system
independent Makefile, which can be run through a program
that will produce a custom Makefile for your site.)
So far, we've only looked at program structure and
modularity. Even more important to assure portability is
the way we use data structures and data types, in memory and
in data files. We'll look at that next time.