Subversion Repositories DevTools

Rev

Rev 6353 | Blame | Compare with Previous | Last modification | View Log | RSS feed

########################################################################
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
#
# Module name   : MakeIf.pm
# Module type   : JATS Internal
# Compiler(s)   : Perl
# Environment(s): JATS
#
# Description   : This modules builds an object that provides
#                 an interface to the underlying toolset
#
#                 It is overly complicated
#
#                       *** DO NOT DETAB ***
#
#......................................................................#

use strict;
use warnings;

#
#   System Globals
#   Declared to avoid compile errors
#
our $ScmVersion;
our $ScmTargetHost;
our @TESTPROJECT_TO_ARUN;
our @TESTPROJECT_TO_URUN;
our $ToolsetPROJECT_type;

###############################################################################
#   MakeInterface ---
#       Interface framework
#
#   Push                Push a package into the interface stack.
#   Factory             Build a interface.
#   Load                Loads the specified interface
#...

package MakeIf;

use strict;
use warnings;

our (@IfStack)              = ();               # interface stack

#-------------------------------------------------------------------------------
# Function        : Makeif::Push
#
# Description     : Push a toolset interface onto a list of such items
#                   Will be used later to construct the toolset interface
#
# Inputs          : *                       - List of packages to push
#
# Returns         : Nothing
#
sub Push
{
    ::Debug( "MakeIf::Push( @_ )" );
    push (@IfStack, @_);                        # push onto stack
}

#-------------------------------------------------------------------------------
# Function        : MakeIf::Factory
#
# Description     : Build a interface.
#                   Creates a Toolset Interface from all the toolsets
#                   that have been pushed on to the list of toolsets
#
#                   The user is provided the reference to the composite
#                   interface created such that it inherits properties from
#                   all the other toolsets
#
# Inputs          : None
#
# Returns         : Refernece to the constructed interface
#
sub Factory
{
    # Add myself to the list of dependent packages
    #   Only really needed in development
    push (@::ScmDepends, __FILE__);

    sub Unroll
    {
        my ($stack) = @_;                       # current
        my ($package, $base, $self);

        #
        #   First package on the stack MUST be the BASE_Toolset
        #   This has a 'Spawn' method that must be invoked
        #
        $package = shift(@$stack);              # base class
        ::Error( "Interface -- base-class $package cannot Spawn." )
            unless( $package->can( "Spawn" ));

        $self = $package->Spawn();

        #
        #   Process the remainder of the toolsets on the stack
        #       1) Cause package to inherit previous package stuff
        #       2) Invoke any local conststructor
        #
        while (scalar(@$stack))
        {                                       # child
            my ($has_constructor);

            $base = $package;
            $package = shift(@$stack);

            no strict 'refs';

            ::Warning( "Interface --- base package $base is empty." )
                unless (%{"$base\::"});

            $has_constructor = $package->can( "Constructor" );

            push @{"$package\::ISA"}, $base;    # inherit class(es)
            bless ($self, $package);            # consecrate

            $self->Constructor()                # 'local' constructor
                if ($has_constructor);
        }

        return ($self);
    }

    my (@stack) = @IfStack;                     # clone stack
    my ($self);

    ::Error( "Interface -- object stack empty." )
        if ( ! scalar (@stack) );
    $self = Unroll( \@stack) ||
        ::Error( "Interface --- cannot build interface object." );
#::DebugDumpData("Factory Stack", \@IfStack );
#::DebugDumpData("Factory", $self );
    return $self;
}

#-------------------------------------------------------------------------------
# Function        : MakeIf::Load
#
# Description     : Loads the specified interface
#                   This function will create a new 'class' based on the
#                   data within the DPACKAGE and DPACKAGE.CFG files
#                   within a consumed package
#
#                   The DPACKAGE file should use the 'Interface()' directive
#                   to add the created package onto the Interface list
#
# Inputs          : $dpackage       - Name of the Package containing file to load
#                   $root           - Base directory of the package
#                   $project        - Package project suffix (optional)
#                   @files          - Files to load
#
# Returns         : Nothing
#
sub Load
{
    my ($dpackage, $root, $project, @files) = @_;

    #
    #   Create the basic template class
    #
    my $if = Template ($dpackage, $project);

    #
    #   Insert essential information into the Interface
    #   Invoke functions within the Interface Class to do this
    #
    $if->setRoot($root);
    $if->ReadFiles( @files );
}

#-------------------------------------------------------------------------------
# Function        : Template
#
# Description     : Create a new skeleton template for a toolset interface
#                   package. The details of the package will be beefed out
#                   later
#
# Inputs          : $dpackage       - Name of the Package containg file to load
#                   $project        - Package project suffix (optional)
#
# Returns         : Nothing
#
sub Template
{
    my ($dpackage, $project) = @_;
    my ($package, $template );

    #
    #   The code will not work if dpackage has a "-" in it
    #   Replace nasty characters with nice ones
    #
    $project = '' unless ( $project );
    $dpackage =~ s~-~_~g;
    $package = $dpackage . "_" . $project . "_DPACKAGE";

    $template = (<< '__END_OF_DPACKAGE_TEMPLATE__');
########################################################################
#
#   DPACKAGE Template -- version (1.x)
#
#       Version         State interface version requirements.
#       Using           Using statement.
#       Needs           Pre-req
#       Interface       Push onto the interface stack.
#       Self            Our reference.
#       Package         Package name.
#       Name            Name of perl package.
#       Root            Root directory of our installation.
#       Debug           General purpose debug output.
#
#   Initialisation
#       setRoot         Configure package Root
#       ReadFiles       Read DPACKAGE files into the class
#..
package __PACKAGE__;

use strict;
use warnings;

our $VERSION='1.00';                            # Template version
our $SelfReference;

DPACKAGE_Initialise();

sub DPACKAGE_Initialise {
    my $self = {
            PACKAGE     => "__DPACKAGE__",
            NAME        => "__PACKAGE__",
            ROOT        => "__ROOT__"
        };
    $SelfReference = bless $self, "__PACKAGE__";
}

sub setRoot             { $_[0]->{ROOT} = $_[1]; }
sub Self                { return $SelfReference; }
sub Package             { $SelfReference->{PACKAGE}; }
sub Name                { $SelfReference->{NAME}; }
sub Root                { $SelfReference->{ROOT}; }
sub Interface           { MakeIf::Push( "__PACKAGE__" ); }
sub Debug               { ::Debug @_; }
sub Debug2              { ::Debug2 @_; }
sub Debug3              { ::Debug3 @_; }
sub Error               { ::Error @_; }


sub Version
{
    my ($major, $minor) = @_;
    Error( "DPACKAGE $SelfReference->{PACKAGE} requires Version($major,$minor)" )
        if ($major != 1 || $minor != 0);
}


sub Using
{
    my ($using) = @_;
    $SelfReference->{USING} = $using;
}


sub Needs
{
    my ($name, $version) = @_;

    #   TODO - statement of DPACKAGE prereqs.
    #
    #   name                Package name
    #
    #   Version [optional]
    #       =xx.xx.xx       Absolute
    #       >=xx.xx.xx      Greater or equal.  Where xx respresents
    #                       either, * or missing.
    #...
}


sub IncDir
{
    #TODO
}

sub LibDir
{
    #TODO
}

sub Libraries
{
    my ($caller, $file, $line) = caller();

    Error( "$SelfReference->{PACKAGE} ($line) requires Using()" )
        if ( ! defined( $SelfReference->{USING} ) );

    MakeIf::Libaries( $SelfReference->{USING}, @_ );
}

#
#   Designed to be called externally though the class interface
#   As the first argument is the class ref
#
sub ReadFiles
{
    my $self = shift;
    my $code;
    foreach my $file ( @_ )
    {
        if ( -e $file )
        {
            open(CFGFILE, $file) || Error( "Cannot open '$file'" );
            $code .= "# line 1 " . $file . "\n";    # name the image
            while (<CFGFILE>) {
                $code .= $_;                        # slurp text
            }
            close (CFGFILE);

            #
            #   Add the file to the makefile dependency list
            #   Really only needed if being sourced from within a sandbox
            #
            push (@::ScmDepends, $file);
        }
    }

    #
    #   Evaluate the code
    #
    eval "$code";
    Error("DPACKAGE Syntax. Package: $SelfReference->{ROOT}", "Err: $@" ) if ($@);
}

sub EvaluateText
{
    my $this = shift;
    eval "@_";
    Error("DPACKAGE Syntax. Package: $SelfReference->{ROOT}", "Err: $@" ) if ($@);
}

###########################################################################
#   -END-
#
__END_OF_DPACKAGE_TEMPLATE__

    $template =~ s/__PACKAGE__/$package/g;      # preprocess template
    $template =~ s/__DPACKAGE__/$dpackage/g;

    #
    #   Have the template for the class in a text string
    #   Create it at run-time
    #
    my $if = eval "# line 1 \"DPACKAGE_TEMPLATE\"\n".   # name the image
         "$template";                                   # .. template code
    ::Error("JATS INTERNAL. MakeIf Template", "Err: $@" )
        if ($@);
    return $if;
}

###############################################################################
#   DPACKAGE Interface
#
#       PackageLoad     Load the DPACKAGES for specified platform(s).
#
#       PackageDirs     Retrieve the search path for package configuration.
#
#       LibUsing        Processing Using(XXX) library recipes
#
#       LibProcess      Apply library ordering rules.
#
#       ExportDepend    Export a local package dependency list.
#..

our %IfPkgLibaries        = ();
our %IfPkgOrdering        =
        (
            'User'      => '0',                 # User (default)

            'App'       => '3',                 # Application common
            'App2'      => '3a',
            'App3'      => '3b',
            'App4'      => '3c',

            'If'        => '5',                 # Interface
            'If2'       => '5a',
            'If3'       => '5b',
            'If4'       => '5c',

            'Kern'      => '7',                 # Kernel (ie MOS, LMOS)
            'Kern2'     => '7a',
            'Kern3'     => '7b',
            'Kern4'     => '7c',

            'Sys'       => '9',                 # System (ie WIN32, Linux)
            'Sys2'      => '9a',
            'Sys3'      => '9b',
            'Sys4'      => '9c'
        );

#
#   Provide reverse lookup of the %IfPkgOrdering for error reporting purposes
#   It may be slow, but its not used (Often).
#
sub ClassNumToText
{
    my (@classes) = @_;
    my @result;

    foreach my $class ( @classes )
    {
        my $result = "Unknown Class($class)";
        foreach ( keys %IfPkgOrdering )
        {
            if ( $IfPkgOrdering{$_} eq $class )
            {
                $result = $_;
                last;
            }
        }
        push @result, $result;
    }
    return @result;
}

#-------------------------------------------------------------------------------
# Function        : PackageLoad
#
# Description     : Scan all external packages and locate DPACKAGE files
#                   This will (must) be in the JATS config directory (gbe)
#
#                   Once found they will be loaded into the system
#                   This will effectivly extend the base toolset
#                   
#                   There are three forms of DPACKAGE file
#                   Manually created and Old format
#                       DPACKAGE
#                   First improvement
#                       DPACKAGE.GBE_MACHTYPE
#                   Second improvement
#                       DPACKAGE_?.cfg
#                       Where ? is the makefile uid number to provide sequencing        
#
# Inputs          : $platform               - target platform
#                                             Only used to locate packages
#                                             Not 'really' used
#
# Returns         : Nothing
#
sub PackageLoad
{
    my ($platform) = @_;
    ::Debug ("PackageLoad", @_);

    return if ( !(%::ScmBuildPkgRules) );# configuration available

    my $pPlatform = $::ScmBuildPkgRules{$platform};
    foreach my $package ( @{$pPlatform} )
    {
        if ( defined( $package->{'CFGDIR'} ) )
        {
            my ($dir) = $package->{'ROOT'}.$package->{'CFGDIR'};
            #
            #   Look for all forms:
            #       DPACKAGE_?.cfg          ( Newer format. Mutally exclusive with New format)
            #       DPACKAGE.GBE_MACHTYPE   ( New format )
            #       DPACKAGE                ( Manually created and Old format )
            #   Use only the first form found
            #
            my $dpackage;
            my @processList;

            # Select the first (newest) form found
            my @newform = glob ("$dir/DPACKAGE_*.cfg");
            if (@newform) {
                @processList = sort @newform;

                #
                #   If consuming files from the interface directory, then they are our own files
                #   Cannot consume ALL of them, only those created by makefiles that have been
                #   created before this one ( Otherwise we start to need multiple parsing of the file )
                #
                if ($package->{'TYPE'} eq 'interface') {
                    my $listIdx = -1;
                    foreach  ( @processList ) {
                        $listIdx++;
                        m~/DPACKAGE_(.*)\.cfg~;
                        my $index = $1;
                        if (int($index) >= int($::ScmMakeUid)) {
                            splice( @processList, $listIdx); 
                            last;
                        }
                    }
                }

            } else {
                $dpackage = "$dir/DPACKAGE." . $::GBE_MACHTYPE;
                if ( -e $dpackage ) {
                    push @processList, $dpackage;
                }
            }

            # This one may always be present
            $dpackage = "$dir/DPACKAGE";
            if ( -e $dpackage ) {
                push @processList, $dpackage;
            }

            if (@processList) {
                ::Debug( "Loading ($package->{'NAME'}) ...", @processList );
                MakeIf::Load( $package->{'NAME'}, $package->{'ROOT'}, $package->{'DPROJ'}, @processList );
            }
        }
    }
}

#-------------------------------------------------------------------------------
# Function        : PackageDirs
#
# Description     : Extend an array of 'search' directories to include
#                   'gbe' extension directories located with external toolsets
#
#                   Scan the package information held in ScmBuildPkgRules
#                   and locate 'gbe' directories.
#
# Inputs          : $dirs       - Ref to an array to extend
#                   @platforms  - A list of platforms to scan
#
# Returns         : Nothing
#
sub PackageDirs
{
    my ($dirs, @platforms) = @_;

    if ( (%::ScmBuildPkgRules) )
    {
        for my $platform (@platforms)
        {
            my $pPlatform = $::ScmBuildPkgRules{$platform};

            foreach my $package ( @$pPlatform )
            {
                push @$dirs, $package->{'ROOT'}.$package->{'CFGDIR'}
                    if ( defined( $package->{'CFGDIR'} ) );
            }
        }
    }
}

#-------------------------------------------------------------------------------
# Function        : MakeIf::Libaries
#
# Description     : Maintain a data structure for the --Using() expansion
#
# Inputs          : $using              - Name of the Macro
#                   $platform           - Active platform predicate
#                   @libs               - List of Libraries
#
# Returns         : Nothing
#
sub Libaries
{
    my ($using, $platforms, @libs) = @_;
    my $recipe;

    ::Debug2( "Makeif::Libraries($using, $platforms, @libs)" );
    return if ( ! ::ActivePlatform($platforms) );

    push @{$IfPkgLibaries{$using}{LIBS}}, @libs;
}


#private
sub LibUsing
{
    my ($caller, $args, $libs) = @_;
    my ($using, @args) = split( ',', $args );

    ::Debug2( "Makeif::LibUsing($caller->$args)" );

    # Global arguments (if any)
    #
    #   ie. --Using(XXX)[,--NoWarn][,--ifdef USING_XXX]
    #
    my ($x_nowarn, $x_conds) = (0, 0);

    foreach (@args) {
        if ( /^--NoWarn$/ ) {                   # .. disable warnings
            $x_nowarn = 1;

        } elsif ( /^--if(.*)$/ ) {              # .. conditional(s)
            $x_conds .= "," if ($x_conds);
            $x_conds .= $_;

        } else {
            ::Warning( "$caller->Using($using), unknown option '$_'" );
        }
    }


    # Sanity test
    #   Recipe Entry Must Exist
    #   Detect recursive invocation
    #
    my $recipe = $IfPkgLibaries{$using};
    unless ( $recipe )
    {
        ::Warning( "$caller->Using($using), module not found" )
            unless ( $x_nowarn );
        return;
    }

    if ( $recipe->{COOKED} ) {
        ::Warning( "$caller->Using($using) causes loop, ignored" )
            unless ($recipe->{COOKED_ERR} );
        $recipe->{COOKED_ERR}++;
        return;
    }

    # Parse Using arguments
    my ($arg, $deforder, $defallowdup, $defnowarn, @defconds);
    $deforder = "User";                 # default "class"
    $defallowdup = 0;
    $defnowarn = 0;

    foreach (@{$recipe->{LIBS}})
    {
        # Locally processed switches (global/default settings)
        #
        if ( /^--DefOrder=(.*)$/ ) {
            $deforder = $1;             # Sort using name
            next;
        }
        if ( /^--DefAllowDup$/ ) {
            $defallowdup = 1;
            next;
        }
        if ( /^--DefNoWarn$/ ) {
            $defnowarn = 1;
            next;
        }

        if ( /^--DefCond\((.*)\)$/ ) {
            my (@conds) = split( ',', $1 );

            @defconds = ();
            foreach (@conds) {
                Error( "Invalid --DeCond argument '$_' " )
                    if ( ! /^--if/ );
                push @defconds, $_;
            }
            next;
        }

        # Handle indirections
        #
        if ( /^--Using\((.*)\)(.*)$/ )
        {                               # recurse
            my ($x_using);

            $x_using  = "$1";
            $x_using .= ",$x_conds"     if ( $x_conds );
            $x_using .= "$2";

            $recipe->{COOKED}++;
            LibUsing( $using, $x_using, $libs );
            $recipe->{COOKED}--;
            next;
        }

        # Libraries and arguments
        #
        if ( /^-[lL].*/ )
        {                               # Library
            my ($order, $allowdup, $nowarn, @conds) =
                ($deforder, $defallowdup, $defnowarn, @defconds);
            my (@args) = split( ',', $_ );

            foreach (@args) {
                if ( /^-([lL])(.*)/ ) {
                    $arg = $_;
                } elsif ( /^--Order=(.*)/ ) {
                    $order = $1;
                } elsif ( /^--AllowDup$/ ) {
                    $allowdup = 1;
                } elsif ( /^--NoAllowDup$/ ) {
                    $allowdup = 0;
                } elsif ( /^--NoWarn$/ ) {
                    $nowarn = 1;
                } elsif ( /^--Warn$/ ) {
                    $nowarn = 0;
                } elsif ( /^--if/ ) {
                    push @conds, $_;
                } else {
                    $arg .= $_;         # unknown
                }
            }

            $arg .= ",--Order=$order"       if ( $order );
            $arg .= ",--NoWarn"             if ( $nowarn );
            $arg .= ",--AllowDup"           if ( $allowdup );
            $arg .= ",$x_conds"             if ( $x_conds );
            $arg .= ",". join(',', @conds)  if ( @conds );
        }
        else
        {                               # other options etc
            $arg = $_;
        }

        push @$libs, $arg;              # push cooked argument
    }
}


sub LibProcess
{
    my ($libs) = @_;
    my (@newlibs, @ordered_newlibs);
    my ($order, @cond, %librecipes, %libstatus);

    #
    #   Only process the library specification if arguments/attributes
    #   are encountered .. compat with older makefile's.
    #
    my ($attrs) = 0;

    #   use Data::Dumper;
    #   $Data::Dumper::Purity = 1;
    #   ::Debug "# Uncooked library list";
    #   ::Debug Dumper( $libs );

    $order = 1;                                 # physical order start @ 1
    @cond = ();

    foreach (@$libs)
    {
        my ($libname, $class, $fdup, $fnowarn, @params);

        $libname = "";                          # library name and class
        $class = "User";                        # deafult class
        $fdup = 0;                              # default, remove duplicates
        $fnowarn = 0;

        if ( /^--if(.*)/ )
        {                                       # Conditional (for next)
            ::Warning( "Stray --if '$1' within library specification -- ignored" )
                if ( @cond );
            @cond = ( $_ );
            $attrs++;
            next;
        }

        if ( /^-[lL].*/ )
        {                                       # Library
            my (@args) = split( ',', $_ );      # split specification

            foreach (@args) {
                if ( /^-([lL])(.*)/ ) {         # Library
                    if ( $::ScmTargetHost eq "Unix" ) {
                        $libname = "lib$2";     # .. prefix lib
                        $libname =~ s/^liblib/lib/;
                    } else {
                        $libname = $2;          # .. just stripped name
                    }
                    $libname .= "\$(GBE_TYPE)"  # .. append GBE_TYPE
                        if ( $1 eq "l" );

                    push @params, $libname;

                } else {                        # Attribute/options
                    if ( /^--Order=(.*)/ ) {    # .. sort using 'class'
                        if ( exists $IfPkgOrdering{$1} )
                        {
                            $class = $1;
                        }
                        else
                        {
                            ::Warning( "Lib '$libname' has unknown class '$1'" );
                        }


                    } elsif ( /^--AllowDup$/ ) {# .. allow duplicate images
                        $fdup = 1;

                    } elsif ( /^--NoWarn$/ ) {  # .. disable warnings
                        $fnowarn = 1;

                    } elsif ( /^--if(.*)/ ) {   # .. conditional(s)
                        push @cond, $_;

                    } else {                    # .. other "parameters"
                        push @params, $_;
                    }

                    $attrs++;
                }
            }

            #
            #   Convert text class name to an ordered number to assist in sorting
            #
            $class = $IfPkgOrdering{ $class };
        }
        else
        {                                       # Anything else
            push @params, $_;
        }

        my ($recipe) = {
                CLASS   => $class,              # logical class, prim key
                ORDER   => $order,              # physical order, 2nd key
                LIBNAME => $libname,            # library name (if any)
                FDUP    => $fdup,               # duplicate flag
                FNOWARN => $fnowarn,            # no-warning flag
                CONDS   => [ @cond ],           # optional conditional(s)
                PARAMS  => [ @params ],         # parameters
            };

        push @newlibs, $recipe;                 # push recipe
        push @{$librecipes{$libname}}, $recipe  # plus index using $name
            if ( $libname ne "" );

        @cond = ();
        $order++;
    }

    #   ::Debug3 "# Cooked library list";
    #   ::Debug3 Dumper( @newlibs );

#   Parse the library list and determine the status and correct
#   handling of any duplicate images.
#..
    foreach my $name (keys %librecipes)
    {
        if ( scalar( @{$librecipes{$name}} ) > 1 )
        {
            my ($class, $classerr, $dup, $nowarn);

            foreach my $recipe (@{$librecipes{$name}})
            {
                $classerr = 1                   # test for conflicts
                    if ( defined($class) && $class ne $recipe->{CLASS} );

                $class = $recipe->{CLASS};

                $dup = $recipe->{FDUP}          # accumulate
                    if ( !defined($dup) || $recipe->{FDUP} );

                $nowarn = $recipe->{FNOWARN}    # accumulate
                    if ( !defined($nowarn) || $recipe->{FNOWARN} );
            }

            my ($stat) = {};

            $stat->{SEEN} = 0;
            $stat->{DUP} = $dup;
            $stat->{NOWARN} = $nowarn;
            $stat->{CLASSERR} = 1 if (defined($classerr));
            $libstatus{$name} = $stat;
        }
    }

#   Sort the library list
#
    @ordered_newlibs =                          # sort by class/order
        sort {
            ($a->{CLASS} ne "" && $b->{CLASS} ne "" &&
                    $a->{CLASS} cmp $b->{CLASS}) ||
            $a->{ORDER} <=> $b->{ORDER}
        } @newlibs;

    #   ::Debug3 "# Sorted library list";
    #   ::Debug3 Dumper( @ordered_newlibs );

#   Now built the final list, removing duplicates (if required)
#
    @$libs = ();                                # zap libraries

    foreach my $newlib (@ordered_newlibs)
    {
        my ($name) = $newlib->{LIBNAME};

        if ($attrs && defined($libstatus{$name}))
        {                                       # two or more entries
            my ($stat) = $libstatus{$name};

            if ( $stat->{SEEN}++ == 0 )
            {                                   # first image
                if ( ! $stat->{NOWARN} )
                {                               # .. warnings ?
                    if ( defined($stat->{CLASSERR}) )
                    {                           # .. class conflict
                        my (@classes);

                        foreach my $recipe (@{$librecipes{$name}}) {
                            ::UniquePush( \@classes, $recipe->{CLASS} );
                        }
                        ::Warning( "Lib '$name' has multiple classes\n\t" .
                            ::CommifySeries( ClassNumToText(@classes) ) );
                    }

                    my ($count) = scalar( @{$librecipes{$name}} );

                    ::Warning( "Lib '$name' appeared $count times, ".
                               "duplicate" . ($count > 2 ? "s":""). " removed" )
                        if ($stat->{DUP} == 0);
                }
            }
            else
            {                                   # remove duplicate(s) ?
                next if ($stat->{DUP} == 0);
            }
        }

        my ($spec);

        $spec = join( ',', @{$newlib->{PARAMS}} );
        $spec .= "\n\t\t" . join( "\n\t\t", @{$newlib->{CONDS}} )
            if ( @{$newlib->{CONDS}} );

        push @$libs, $spec;
    }

    #   ::Debug "# Final library list";
    #   ::Debug Dumper( $libs );
}


sub ExportDepend
{
    #
    #   Create the dependancy data structure
    #   Its easier to dump a complete structure
    #
    my %ScmBuildPkgDepend;
    foreach my $platform ( keys %::ScmBuildPkgRules )
    {
        foreach my $package ( @{$::ScmBuildPkgRules{$platform}} )
        {
            my %entry;
            $entry{NAME}        = $package->{'NAME'};
            $entry{VERSION}     = $package->{'VERSION'};
            $entry{DNAME}       = $package->{'DNAME'};
            $entry{DVERSION}    = $package->{'DVERSION'};
            $entry{DPROJ}       = $package->{'DPROJ'};

            push @{$ScmBuildPkgDepend{$platform}}, \%entry;
        }
    }


    my $fh = ::ConfigurationFile::New( "DPACKAGE.CFG" );
    $fh->Header( "makelib (Version $::ScmVersion)",
                                "DPACKAGE configuration file" );
    $fh->DumpData(
        "# Dependancy list.\n#\n",
        "ScmBuildPkgDepend", \%ScmBuildPkgDepend );

    $fh->Close(1);
}


###############################################################################
#   BASE_Toolset ---
#       Default interface.
#
#..

package BASE_Toolset;

MakeIf::Push( "BASE_Toolset" );                 # root of object stack

sub Spawn
{
    my ($obclass) = shift;
    my ($class) = ref($obclass) || $obclass;
    my ($self) = {};

    ::Debug2( "\tBASE_Toolset->Spawn()" );
    bless($self, $class);
    return ($self);
}


sub Preprocess
{
    my ($self) = shift;

    ::Debug2( "BASE_Toolset->Preprocess()" );
    ::ToolsetPreprocess()                       # optional toolset interface
        if ( defined &::ToolsetPreprocess );
}


sub Postprocess
{
    my ($self) = shift;

    ::Debug2( "BASE_Toolset->Postprocess()" );
    ::ToolsetPostprocess()                      # optional toolset interface
        if ( defined &::ToolsetPostprocess );
}


sub CTAGS
{
    ::ToolsetCTAGS()                            # optional toolset interface
        if ( defined &::ToolsetCTAGS );
}


sub CCDepend
{
    my ($self) = shift;

    ::Debug2( "BASE_Toolset->CCDepend()" );
    ::Error ("BASE_Toolset does not support C file dependency generation" )
        unless ( defined &::ToolsetCCDepend );
    ::ToolsetCCDepend( @_ );                    # toolset
}


sub CXXDepend
{
    my ($self) = shift;

    ::Debug2( "BASE_Toolset->CXXDepend()" );
    ::Error ("BASE_Toolset does not support C++ dependency generation" )
        unless ( defined &::ToolsetCXXDepend );
    ::ToolsetCXXDepend( @_ );                   # toolset
}


sub CC
{
    my ($self) = shift;
    my ($source, $obj, $pArgs) = @_;

    ::Debug2( "BASE_Toolset->CC($source)" );
    ::Error ("BASE_Toolset does not support C compilation" )
        unless ( defined &::ToolsetCC );
    ::ToolsetCC( @_ );                          # toolset
}


sub CXX
{
    my ($self) = shift;
    my ($source, $obj, $pArgs) = @_;

    ::Debug2( "BASE_Toolset->CXX($source)" );
    ::Error ("BASE_Toolset does not support C++ compilation" )
        unless ( defined &::ToolsetCXX );
    ::ToolsetCXX( @_ );                         # toolset
}


sub AS
{
    my ($self) = shift;
    my ($source, $obj, $pArgs) = @_;

    ::Debug2( "BASE_Toolset->AS($source)" );
    ::Error ("BASE_Toolset does not support assembler files" )
        unless ( defined &::ToolsetAS );
    ::ToolsetAS( @_ );                          # toolset
}


sub EXT
{
    my ($self) = shift;

    ::Debug2( "BASE_Toolset->EXT()" );
    return ::ToolsetEXT( @_ )                   # optional toolset interface
        if ( defined &::ToolsetEXT );
    return 0;                                   # default not supppored
}


sub AR
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs) = @_;

    ::Debug2( "BASE_Toolset->AR($name)" );
    ::Error ("BASE_Toolset does not support library files" )
        unless ( defined &::ToolsetAR );

    ProcessArgs ( "", @_ );                     # Proccess arguments
    ::ToolsetAR( @_ );                          # toolset
}


sub ARLINT
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs) = @_;

    my ($lname) = "\$(LIBDIR)/$name\$(GBE_TYPE)";
    my ($ltarget) = "lib_$name\_lint";
    my (@LIBCSRC, @LIBCXXSRC) = ();
    my ($srcfile) = "";

    ::Debug2( "BASE_Toolset->ARLINT($name)" );
    ::Error ("BASE_Toolset does not support library files" )
        unless ( defined &::ToolsetAR );

    foreach (@$pObjs) {
        $_ =~ s/^\$\(OBJDIR\)\///g;             # remmove OBJDIR

        if ( ($srcfile = $::OBJSOURCE{ $_ }) )
        {
            my( $quoted_srcfile ) = quotemeta( $srcfile );

            if ( grep /^$quoted_srcfile$/, @::CSRCS ) {
                push( @LIBCSRC, $srcfile );

            } elsif ( grep /^$quoted_srcfile$/, @::CXXSRCS ) {
                push( @LIBCXXSRC, $srcfile );

            }
        }
    }

    ::MakePrint(  "
#.. Lint library ($name)

.PHONY:         $ltarget
$ltarget:       LIBBASE = $name
$ltarget:       LIBNAME = $lname
" );

    ::MakeDefEntry ( "$ltarget:","LIBCSRC =", \@LIBCSRC );
    ::MakeDefEntry ( "$ltarget:","LIBCXXSRC =", \@LIBCXXSRC );

    if ( defined ( &::ToolsetARLINT ) ) {
        ::MakePrint( "$ltarget:" );
        ::ToolsetARLINT( $name, $pArgs, $pObjs, $ltarget, \@LIBCSRC, \@LIBCXXSRC );

    } else {
        ::MakePrint( "$ltarget: \
        \@echo '[\$(LIBNAME)] linting not supported..'\n" );
    }

    ::MakePrint( "\n" );
}


sub ARMerge
{
    my ($self) = shift;
    my ($name, $pArgs, $pLibs) = @_;

    ::Debug2( "BASE_Toolset->ARMERGE($name)" );
    ::Error ("BASE_Toolset does not support library file merging" )
        unless ( defined &::ToolsetARMerge );
    ::ToolsetARMerge( @_ );                     # toolset
}


sub SHLD
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs, $pLibs) = @_;
    my ($i);

    ::Debug2( "BASE_Toolset->SHLD($name)" );
    ::Error ("BASE_Toolset does not support shared libraries" )
        unless ( defined &::ToolsetSHLD );

    ProcessArgs ( 'Using', @_ );                # Proccess arguments

    MakeIf::LibProcess( $pLibs );               # cook libraries

    ::Debug3( " args: @$pArgs" );
    ::Debug3( " objs: @$pObjs" );
    ::Debug3( " libs: @$pLibs" );

    ::ToolsetSHLD( @_ );                        # toolset
}


sub SHLDLINT
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs, $pLibs) = @_;

    my ($lname) = "\$(LIBDIR)/$name\$(GBE_TYPE)";
    my ($ltarget) = "shlib_$name\_lint";
    my (@SHLIBCSRC, @SHLIBCXXSRC) = ();
    my ($srcfile) = "";

    ::Debug2( "BASE_Toolset->SHLDLINT($name)" );
    ::Error ("BASE_Toolset does not support shared libraries" )
        unless ( defined &::ToolsetSHLD );

    foreach (@$pObjs) {
        $_ =~ s/^\$\(OBJDIR\)\///g;             # remmove OBJDIR

        if ( ($srcfile = $::OBJSOURCE{ $_ }) )
        {
            my( $quoted_srcfile ) = quotemeta( $srcfile );

            if ( grep /^$quoted_srcfile$/, @::CSRCS ) {
                push( @SHLIBCSRC, $srcfile );

            } elsif ( grep /^$quoted_srcfile$/, @::CXXSRCS ) {
                push( @SHLIBCXXSRC, $srcfile );

            }
        }
    }

    ::MakePrint(  "
#.. Lint shared library ($name)

.PHONY:         $ltarget
$ltarget:       SHLIBBASE = $name
$ltarget:       SHLIBNAME = $lname
" );

    ::MakeDefEntry ( "$ltarget:","SHLIBCSRC =", \@SHLIBCSRC );
    ::MakeDefEntry ( "$ltarget:","SHLIBCXXSRC =", \@SHLIBCXXSRC );

    if ( defined ( &::ToolsetSHLDLINT ) ) {
        ::MakePrint( "$ltarget:" );
        ::ToolsetSHLDLINT( $name, $pArgs, $pObjs, $pLibs, $ltarget, \@SHLIBCSRC, \@SHLIBCXXSRC );

    } else {
        ::MakePrint( "$ltarget: \
        \@echo '[\$(SHLIBNAME)] linting not supported..'\n" );
    }

    ::MakePrint( "\n" );
}


sub LD
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs, $pLibs) = @_;
    my ($i);

    ::Debug2( "BASE_Toolset->LD($name)" );
    ::Error ("BASE_Toolset does not support program generation" )
        unless ( defined &::ToolsetLD );

    ProcessArgs ( 'Using', @_ );                # Proccess arguments

    MakeIf::LibProcess( $pLibs );               # cook libraries

    ::Debug3( " args: @$pArgs" );
    ::Debug3( " objs: @$pObjs" );
    ::Debug3( " libs: @$pLibs" );

    ::ToolsetLD( @_ );                          # toolset
}


sub LDLINT
{
    my ($self) = shift;
    my ($name, $pArgs, $pObjs, $pLibs) = @_;

    my ($pname) = "\$(BINDIR)/$name";
    my ($ptarget) = "prog_$name\_lint";
    my (@PROGCSRC, @PROGCXXSRC) = ();
    my ($srcfile) = "";

    ::Debug2( "BASE_Toolset->LDLINT($name)" );
    ::Error ("BASE_Toolset does not support program generation" )
        unless ( defined &::ToolsetLD );

    foreach (@$pObjs) {
        $_ =~ s/^\$\(OBJDIR\)\///g;             # remmove OBJDIR

        if ( ($srcfile = $::OBJSOURCE{ $_ }) )
        {
            my( $quoted_srcfile ) = quotemeta( $srcfile );

            if ( grep /^$quoted_srcfile$/, @::CSRCS ) {
                push( @PROGCSRC, $srcfile );

            } elsif ( grep /^$quoted_srcfile$/, @::CXXSRCS ) {
                push( @PROGCXXSRC, $srcfile );

            }
        }
    }

    ::MakePrint(  "
#.. Lint program ($name)

.PHONY:         $ptarget
$ptarget:       PROGBASE = $name
$ptarget:       PROGNAME = $pname
" );

    ::MakeDefEntry ( "$ptarget:","PROGCSRC =", \@PROGCSRC );
    ::MakeDefEntry ( "$ptarget:","PROGCXXSRC =", \@PROGCXXSRC );

    if ( defined ( &::ToolsetLDLINT ) ) {
        ::MakePrint( "$ptarget:" );
        ::ToolsetLDLINT( $name, $pArgs, $pObjs, $pLibs, $ptarget, \@PROGCSRC, \@PROGCXXSRC );

    } else {
        ::MakePrint( "$ptarget: \
        \@echo '[\$(PROGNAME)] linting not supported..'\n" );
    }

    ::MakePrint( "\n" );
}

#-------------------------------------------------------------------------------
# Function        : PROJECT
#
# Description     : Invoke the underlying toolset functions to construct a
#                   project
#
# Inputs          : $pProject       - Reference to the project data
#
# Returns         :
#
sub PROJECT
{
    my( $self, $pProject) = @_;
    my $name = $pProject->{name};

    ::Debug2( "BASE_Toolset->PROJECT($name)" );
    ::Error ("BASE_Toolset does not support Project generation" )
        unless ( defined &::ToolsetPROJECT );

    #
    #   Sanity test the Toolset PROJECT
    #   Only the JAVA toolset will handle ANT projects
    #
    my $ptype = $pProject->{type} ;
    if ( $ptype )
    {
        my $type = $::ToolsetPROJECT_type || '';
        ::Error ("BASE_Toolset does not support Project generation of type: $ptype " )
            unless ( $type eq $ptype );
    }

    ::Error ("BASE_Toolset does not support Automated Unit Tests" ) 
        if ($pProject->{'autotest'} && ! $::ScmToolsetProperties{'AutoUnitTests'}); 
    ::Error ("BASE_Toolset does not support Manual Unit Tests" ) 
        if ($pProject->{'unittest'} && ! $::ScmToolsetProperties{'UnitTests'});

    #
    #   Generate Phony target names
    #   The toolset MUST provide the targets
    #
    ::MakePrint( "PHONY:\tProjectClean_$name\n" );
    ::MakePrint( "PHONY:\tProject_$name\n" );
    ::MakePrint( "PHONY:\tProjectATest_$name\n" ) if ($pProject->{'autotest'});
    ::MakePrint( "PHONY:\tProjectUTest_$name\n" ) if ($pProject->{'unittest'});
    ::MakePrint( "\n" );

    #
    #   Projects may be specific to debug or production
    #
    my $conditional;
    $conditional = 'ifeq'  if ($pProject->{'Debug'});
    $conditional = 'ifneq' if ($pProject->{'Prod'});
    if ( $conditional )
    {
        ::MakePrint( "$conditional \"\$(DEBUG)\" \"1\"\n" );
    }

    #
    #   Invoke toolset specific function to create the desired targets
    #
    ::ToolsetPROJECT( $pProject->{name},
                      $pProject->{project},
                      $pProject->{options},
                      $pProject->{'autotest'},
                      $pProject->{'unittest'},
                      $pProject->{'generated'},
                      $pProject
                       );

    #
    #   Complete any conditional process
    #
    if ( $conditional )
    {
        ::MakePrint( "else\n\n" );
        ::MakePrint( "ProjectClean_$name:\n" );
        ::MakePrint( "Project_$name:\n" );
        ::MakePrint( "ProjectATest_$name:\n" ) if ($pProject->{'autotest'});
        ::MakePrint( "ProjectUTest_$name:\n" ) if ($pProject->{'unittest'});
        ::MakePrint( "\nendif\n" );
    }

    #
    #   If this project does contain a unit test, then the name of the target
    #   that will run the unit test must be added to a list
    #
    push @::TESTPROJECT_TO_ARUN, "ProjectATest_$name" if ($pProject->{'autotest'});
    push @::TESTPROJECT_TO_URUN, "ProjectATest_$name" if ($pProject->{'autotest'});
    push @::TESTPROJECT_TO_URUN, "ProjectUTest_$name" if ($pProject->{'unittest'});
}


#-------------------------------------------------------------------------------
# Function        : TESTFRAMEWORK
#
# Description     : Invoke the underlying toolset functions to construct a
#                   Test Frame Work
#
#                   Done by manipulating entries in the HASH that is passed
#                   into the FrameWork Support functionn
#
# Inputs          : $pEntry       - Reference to the test data
#
# Returns         :
#
sub TESTFRAMEWORK
{
    my( $self, $pEntry, @args) = @_;
    my $name = $pEntry->{framework};
    my $fnc = 'ToolsetTESTFRAMEWORK_'. uc($name);
    my $fref = UNIVERSAL::can ( '::main', $fnc );
    ::Debug2( "BASE_Toolset->TestFrameWork($name)" );
    ::Error ("BASE_Toolset does not support Test Frame Work generation: $name" )
        unless ( $fref );

    &$fref( $pEntry, @args );          # Invoke the Function
}

#sub LDUsing
#{
#    my ($self) = shift;
#    my ($using, $name, $isshared, $pObjs, $pLibs) = @_;
#
#    ::Debug2( "BASE_Toolset->LDUsing($using, $isshared, $name)" );
#
#    MakeIf::LibUsing( $name, $using, $pLibs );
#}

#-------------------------------------------------------------------------------
# Function        : ProcessArgs
#
# Description     : Process arguments for the program and library building commands
#                   Processes options:
#                       --Using(XXX),[options]
#                       --Exclude(XXX,XXX,XXX)
#                       --ExcludeLIB(XXX,XXX,XXX)
#
# Inputs          : $mode       - Using == Allow 'Using'
#                   $name       - Name of the program/library
#                   $pArgs      - Ref to an array of arguments
#                   $pObjs      - Ref to an array of objects
#                   $pLibs      - Ref to an array of libraries (Optional)
#
#                   Note: Argument order to assist caller
#
# Returns         : Nothing directly
#                   Massages arrays directly
#
sub ProcessArgs
{
    my ($mode, $name, $pArgs, $pObjs, $pLibs) = @_;
    ::Debug2( "BASE_Toolset->ProcessArgs(@$pArgs)" );

    my @rArgs;

    my $i;
    for ($i = 0; $i < scalar @$pArgs; $i++)
    {
        $_ = $pArgs->[$i];

        if ( $mode && /^--Using\((.*)\)(.*)$/)  # --Using(XXX)[,options]
        {
            MakeIf::LibUsing( $name, "$1$2", $pLibs );

#::DebugDumpData("IfPkgLibaries",\%IfPkgLibaries );
#::DebugDumpData("pLibs",\$pLibs );
        }
        elsif (/^--Exclude\((.*)\)$/)           # --Exclude(Obj,Obj...)
        {
            #
            #   Exclude specified object files
            #   Note: Maintain the order of the object files
            #
            my %exclude;
            my @retain = ();

            @exclude{split( ',', $1 )} = ();

            foreach (@$pObjs)
            {
                (my $objfile = $_) =~ s~.*/~~;  # Remove Shared lib object directory

                push @retain, $_
                    unless (exists $exclude{$objfile} );
            }
            @$pObjs = @retain;
            ::Debug3( "BASE_Toolset->ProcessArgs. OBJS: @retain)" );
        }
        elsif (/^--ExcludeLib\((.*)\)$/)        # --ExcludeLib(Lib,Lib...)
        {
            #
            #   Exclude specified library files
            #   Note: Maintain the order of the library files
            #
            my %exclude;
            my @retain_libs = ();

            @exclude{split( ',', $1 )} = ();

            foreach (@$pLibs)
            {
                m~-[Ll]([^,]*)~;                # Extract library name
                my $libfile = $1;
                push @retain_libs, $_
                    unless (exists $exclude{$libfile} );
            }
            @$pLibs = @retain_libs;
            ::Debug3( "BASE_Toolset->ProcessArgs. LIBS: @retain_libs)" );
        }
        else
        {
            #
            #   Unknown option
            #   Retain to be processed later
            #
            push @rArgs, $_;
        }
    }

    #
    #   Pass unprocessed arguments back to the user
    #
    @$pArgs = @rArgs;
}

1;