#!/usr/bin/perl
#
# Revision History:
#
#   25-Nov-2002 Dick Munroe (munroe@csworks.com)
#       Add ix86 with Grub support.
#       Break the packages out into separate files and directories.
#
#   26-Nov-2002 Dick Munroe (munroe@csworks.com)
#       Change the order of construction so that cleaning happens first.
#
#   31-Dec-2002 Dick Munroe (munroe@csworks.com)
#       Add documentation.
#       Add -h and --help switches.
#
#   18-May-2003 Dick Munroe (munroe@csworks.com)
#       Fixes for potential problems with leakage of package variables in
#       all helper classes.
#
#   19-May-2003 Dick Munroe (munroe@csworks.com)
#       Add a version switch.
#

use 5.8.0 ;
use strict ;

use Build::alpha ;
use Build::ix86 ;
use Build::ppc ;
use Getopt::Long qw(:config bundling) ;
use Pod::Usage ;
use StanzaFile ;

our $VERSION = "1.02" ;

#
# The IK Actions vary depending on architecture, but are basically
# a way to determine what the user wants to do.  The overall flow
# of control (by default) is:
#
#       Make clean
#       Make Dependencies
#       Make Kernel
#       Make Modules
#       Make Modules Install
#       Modify Bootloader
#       Move files
#           Kernel
#           Compressed kernel (generated from kernel via gzip)
#           System.map
#           Configuration file
#       Move kernel files.
#       Make symbolic links
#       Make kernel module dependencies
#       Update initrd
#
# The start and end are parameters in argv as follows:
#

my @theIKActions = ('clean',
		    'dependencies',
		    'kernel',
		    'modules',
		    'modules_install',
		    'bootloader',
		    'movefiles',
		    'links',
		    'depmod',
		    'initrd') ;

sub actionIndex
{
    my $theString = shift ;
    my $theIndex = 0 ;
    
    foreach (@theIKActions)
    {
	return $theIndex if (m/^$theString/i) ;
	$theIndex++ ;
    } ;

    die "Unknown action: $theString" ;
} ;

my $theBuildArchitecture = `uname -m` ;
my $theBuildDirectory = `pwd` ;
my $theHelpFlag ;
my $theLogFile ;
my $theNoBootloaderFlag ;
my $theNoCleanFlag ;
my $theNoDependenciesFlag  ;
my $theNoDepmodFlag  ;
my $theNoInitrdFlag ;
my $theNoKernelFlag ;
my $theNoLinksFlag ;
my $theNoModulesFlag ;
my $theNoModulesInstallFlag ;
my $theNoMovefilesFlag ;
my $theTestFlag ;
my $theVerboseFlag ;
my $theVersionFlag ;

chomp($theBuildArchitecture) ;
chomp($theBuildDirectory) ;

GetOptions('architecture|a=s' => \$theBuildArchitecture,
	   'directory|d=s'    => \$theBuildDirectory,
	   'help|h'           => \$theHelpFlag,
	   'log=s'            => \$theLogFile,
	   'nobootloader'     => \$theNoBootloaderFlag,
	   'noclean'          => \$theNoCleanFlag,
	   'nodepmod'         => \$theNoDepmodFlag,
	   'nodependencies'   => \$theNoDependenciesFlag,
	   'noinitrd'         => \$theNoInitrdFlag,
	   'nokernel'         => \$theNoKernelFlag,
	   'nolinks'          => \$theNoLinksFlag,
	   'nomodules'        => \$theNoModulesFlag,
           'nomodules_install' => \$theNoModulesInstallFlag,
	   'nomoveflags'      => \$theNoMovefilesFlag,
	   'test'             => \$theTestFlag,
	   'verbose|v+'       => \$theVerboseFlag,
	   'version|V'        => \$theVersionFlag) ;

#
# If help was asked for, just print it out.
#

if ($theHelpFlag)
{
    pod2usage({ -message => "Usage: kif\n", -exitval => 2, -verbose => 2 }) ;
} ;

#
# If the version was asked for, just print it out.
#

if ($theVersionFlag)
{
    print $VERSION,"\n" ;
    exit ;
} ;

#
# The default ik activity is to run all actions.
#

if (scalar(@ARGV) < 1)
{
    $ARGV[0] = 'clean' ;
} ;

if (scalar(@ARGV) < 2)
{
    $ARGV[1] = 'initrd' ;
} ;

my $theBeginningActionIndex = &actionIndex($ARGV[0]) ;
my $theEndingActionIndex = &actionIndex($ARGV[1]) ;

#
# Actions must appear in order.
#

die "Actions $theIKActions[$theBeginningActionIndex] and $theIKActions[$theEndingActionIndex] are out of order" 
    if ($theBeginningActionIndex > $theEndingActionIndex) ;

#
# Now set up the actions on a per architecture basis.
#

my $theBuildObject ;

$theBuildObject = new Build::alpha(directory => $theBuildDirectory,
				   log => $theLogFile,
				   test => $theTestFlag,
				   verbose => $theVerboseFlag) if ($theBuildArchitecture eq 'alpha') ;

$theBuildObject = new Build::ix86(directory => $theBuildDirectory,
				  log => $theLogFile,
				  test => $theTestFlag,
				  verbose => $theVerboseFlag) if ($theBuildArchitecture =~ m/^i\d86$/) ;

$theBuildObject = new Build::ppc(directory => $theBuildDirectory,
				 log => $theLogFile,
				 test => $theTestFlag,
				 verbose => $theVerboseFlag) if ($theBuildArchitecture eq 'ppc') ;

#
# Make sure common bits and pieces of the build environment are sane.
#

$theBuildObject->validate() ;

#
# Validate the boot configuration files.
#

$theBuildObject->bootloader->validate($theBuildObject) ;

#
# Run the build and installation.
#

my $theIndex ;

for ($theIndex = $theBeginningActionIndex; $theIndex <= $theEndingActionIndex; $theIndex++)
{
  SWITCH:
    {
	if (($theIndex == 0) && (!$theNoCleanFlag)) { $theBuildObject->doClean(); last SWITCH; } ;
	if (($theIndex == 1) && (!$theNoDependenciesFlag)) { $theBuildObject->doDependencies(); last SWITCH; } ;
	if (($theIndex == 2) && (!$theNoKernelFlag)) { $theBuildObject->doKernel(); last SWITCH; } ;
	if (($theIndex == 3) && (!$theNoModulesFlag)) { $theBuildObject->doModules(); last SWITCH; } ;
	if (($theIndex == 4) && (!$theNoModulesInstallFlag)) { $theBuildObject->doModules_install(); last SWITCH; } ;
	if (($theIndex == 5) && (!$theNoBootloaderFlag)) { $theBuildObject->doBootloader(); last SWITCH; } ;
	if (($theIndex == 6) && (!$theNoMovefilesFlag)) { $theBuildObject->doMovefiles(); last SWITCH; } ;
	if (($theIndex == 7) && (!$theNoLinksFlag)) { $theBuildObject->doLinks(); last SWITCH; } ;
	if (($theIndex == 8) && (!$theNoDepmodFlag)) { $theBuildObject->doDepmod(); last SWITCH; } ;
	if (($theIndex == 9) && (!$theNoInitrdFlag)) { $theBuildObject->doInitrd(); last SWITCH; } ;
    } ;
} ;

=pod

=head1 NAME

kif - kernel installation facility

=head1 SYNOPSIS

Automate the building and installation of Linux kernels.  Manage the necessary
configuration files for each installed Linux kernel.

kif [options] [startingPhase [endingPhase]]

=head1 DESCRIPTION

kif and it's associated perl modules provide a framework for building and
installing Linux kernels.  It is extensible and can be made to handle any
combination of architectures and boot loaders.  Version 1.00 handles:

=over

=item *

ppc (Macintosh), BootX

=item *

alpha, aboot

=item *

ix86, GRUB and LILO

=back

any and all extensions made for other processor architectures and
boot loaders will be greatfully included.

The kernel installation process is broken into "phases".  These phases define
what kif is to do.  Be default kif begins at the first phase and goes to the
last.  kif may be instructed to begin and end at any desired phase.  The phases
and their purposes are:

=over

=item *

clean - [default starting phase if no starting phase is specified]
from the build directory (by default, the current directory) save a 
copy of .config and include/linux/autoconf.h, clean the directory (distclean is
used), and restore .config and include/linux/autoconf.h.  .config and
autoconf.h may be missing although if you haven't generated a valid .config
file for the current build, then do so before continuing.  The timestamps
of all files are maintained to preserve any time dependent tests done by
the kernel build itself.

If there is no valid autoconf.h or the .config file is newer, then the
autoconf.h file is regenerated using "make oldconfig".

=item *

dependencies - using the current .config and autoconf.h rebuild the 
dependencies in the build directory.  This is done via "make dep".

=item *

kernel - compile and link the kernel.  This is done via the appropriate
architecture dependent make command, e.g., make bzImage for ix86 architectures.

=item *

modules - compile and link the specified modules.  This is done via "make
modules".

=item *

modules_install - install the modules in the appropriate directory beneath
/lib/modules.  Remember to modify buildDirectory/Makefile to include the
appropriate version information to get the modules put in the "right" place.

=item *

bootloader - modify the bootloader configuration file(s) to make the new kernel
the default bootable image.  This is also the first place in which the current
system gets altered, e.g., an ix86 architecture using lilo will have it's boot
block modified at this point.  The original bootloader configuration files are
saved with an extension of .old.

=item *

movefiles - .config, autoconf.h, System.map, vmlinux, and any architecture
dependent files are copied to the appropriate place(s) and tagged with the
current release information to make the unique and identifiable.

=item *

links - all necessary symbolic links are constructed.

=item *

depmod - The necessary system module dependency information is build using the
appropriate System.map for the new kernel.

=item *

initrd - [default ending phase if no ending phase is specified]
Generate the initrd file for this kernel image if an initrd file
for this kernel already exists or the bootloader configuration file defines
an initrd file for this image.

=back

=head1 OPTIONS

=over

=item -a architecture | --architecture=architecture

By default the current architecture.  Used to select the appropriate perl
modules for building on the specified architecture.  The value of this
option may be any valid Linux architecture as returned by "uname -a".  Not
all valid architectures may be supported at any given time.

=item -d buildDirectory | --directory=buildDirectory

The path to the directory containing the source for the Linux kernel and
modules.  If omitted it defaults to the current working directory.

=item -h | --help

Produce this pod.

=item --log=logfile

The path to the file into which the output of kif is to be written.

=item Phase Suppression Options - are of the form noPhaseName where PhaseName
is any of the valid phases.  Inclusion of one of these options causes the
specified phase of the build process to be skipped if it would normally be
executed.

=over

=item --nobootloader

=item --noclean

=item --nodepmod

=item --nodependencies

=item --noinitrd

=item --nokernel

=item --nolinks

=item --nomodules

=item --nomodules_install

=item --nomoveflags

=back

=item --test

Run kif but do not execute any commands.  This is mostly useful in conjunction
with the verbose option, below.

=item -v | --verbose

These may be used more than once to increase the verbosity of the output.

=item -V | --version

Print the current version number.

=back

=head1 FILES

All files saved or written by kif will be "tagged" with the version information
extracted from the make file present in the build directory.  It is the
responsibility of the user to modify the make file as appropriate to allow
kif to work sanely.  The collection of information defining the current
release of the kernel is called the "releaseTag".

Not all of the following files exist on each system.  Not all systems use
/boot as the place to store kernel images.

=over

=item /boot/etc/aboot.conf 

The aboot configuration file used to boot this 
kernel.

=item /boot/autoconf.h-releaseTag 

The autoconf.h generated when this kernel
was built.

=item /boot/config-releaseTag 

The .config file used to generate autoconf.h
when this kernel was built.

=item /boot/grub/grub.conf 

The grub configuration file used to boot this 
kernel.

=item /boot/initrd-releaseTag.img 

The initrd file genereated for this kernel.

=item /boot/System.map-releaseTag 

The system map for this kernel.

=item /boot/vmlinux 

Symbolic link to the appropriate default uncompressed 
Linux kernel.

=item /boot/vmlinux-releaseTag 

The uncompressed Linux kernel image.

=item /boot/vmlinuz 

Symbolic link to the appropriate default compressed
Linux kernel.

=item /boot/vmlinuz-releaseTag 

The compressed Linux kernel image.

=item /etc/lilo.conf 

The lilo configuration file used to boot this kernel.

=item /lib/modules/releaseTag/

The modules associated with the Linux kernel.

=back

=head1 Getting kif

The CVS sources and release kits are available from SourceForge at:

    http://sf.net/projects/kif

The latest released version is also available from my PAUSE directory at:

    http://backpan.cpan.com/authors/id/M/MU/MUNROER/kif

The latest development version is available from my website at:

    http://www.csworks.com/download/

=head1 Author

Dick Munroe (munroe@csworks.com).  I'm looking for work (contract or permanent).  I
do a lot more than just hack Perl.  Take a look at my:

Resume:	http://www.csworks.com/resume
Skills:	http://www.csworks.com/skills
CV:	http://www.csworks.com/cv

for the gory details.  If you see a match, drop me a not and we'll see what we
can work out.

=cut

