Classic Shell Scripting - Arnold Robbins [113]
find_file $srcdir/$subdir/$1.tgz "tar xfz" && return
find_file $srcdir/$subdir/$1.zip "unzip -q" && return
find_file $srcdir/$subdir/$1.jar "jar xf" && return
done
done
}
It is evident from the inner loop body that find_package recognizes multiple archive formats, and that another function, find_file, is called upon to do the real work: when it succeeds, we can immediately return. In the second iteration of the inner loop, subdir is empty, and the pathnames have two consecutive slashes—but that is harmless, as discussed in Section B.4.1 in Appendix B. While this code has superficial similarity to the pathfind command in Example 8-1, here we need to look for several files in each directory, and do different things for each one.
We noted at the beginning of this section that the .tar.gz archive format is common. However, other compression and naming schemes are also found. tar is primarily a Unix command, and although implementations for other operating systems exist, they aren't included in standard distributions. The InfoZip format[3] was developed collaboratively with the goal of supporting compressed archives that can be used on any operating system, and Java jar [4] files also use the InfoZip format. The loop body in find_package handles all of them.
At a small site, it may be reasonable to store package archives in a single directory, such as /usr/local/src. However, as the archive collection grows, that organization soon becomes unwieldy. At our sites, each package is given its own source directory so, for example, the archive for Version 3.1.4 of gawk is stored in /usr/local/gnu/src/gawk/gawk-3.1.4.tar.gz, and the build logs for that version are stored in /usr/local/gnu/src/gawk/logs/gawk-3.1.4. A WHERE-FROM file in each package directory records the package's Internet master archive location, making it easy to check for newer releases. We generally keep the last few versions of each archive around, because they might be necessary someday to rebuild a package when the network is not available or the remote master archive site is not reachable. Thus, the loop body in find_package strips the version number from the package name, storing the result in base, and it first tries to find packages in $srcdir/$base before falling back to looking in $srcdir.
We have found it quite useful to retain build logs, since investigation of a bug that shows up long after installation may need details of which compiler and options were used. Also, with less-portable packages, it is often necessary to make minor tweaks to the build process, or even to source files, to get a build to complete. If that information is recorded in log files, it can save the installer time later when newer versions of those packages need to be built and installed.
The find_file function is essentially just a readability and existence test for the package archive file, the recording of its arguments in two global variables, and the return of a status result. It simplifies the code in find_package considerably:
find_file( )
{
# Usage:
# find_file file program-and-args
# Return 0 (success) if found, 1 (failure) if not found
if test -r "$1"
then
PAR="$2" Program and arguments to use for extraction
PARFILE="$1" Actual file to extract source from
return 0
else
return 1
fi
}
The set_userhosts function provides the convenience of allowing userhosts files to be specified with explicit paths, possibly relative to the current directory, or found in the $BUILDHOME initialization directory. This makes it convenient to create sets of build hosts grouped by compiler, platform, or package, in order to accommodate packages that are known to build only in certain limited environments. Any number of userhosts files can be provided, so we simply accumulate their names in ALTUSERHOSTS:
set_userhosts( )
{
# Usage: set_userhosts file(s)
for u in "$@"
do
if test -r "$u"
then
ALTUSERHOSTS="$ALTUSERHOSTS $u"
elif test -r "$BUILDHOME/$u"
then
ALTUSERHOSTS="$ALTUSERHOSTS $BUILDHOME/$u"
else
error