Symbian/SysDefToText/SysDefCollector.pm
author Simon Howkins <simonh@symbian.org>
Mon, 01 Mar 2010 14:02:11 +0000
branchCompilerCompatibility
changeset 9 f249c5589d66
parent 0 beb51793110d
permissions -rw-r--r--
Bug 1400: [GCCE] GCCE 4.X strictness for "extra qualification" error Added a "permissive" variant for use with GCC 4.4.1.

#
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
#
#-------------------------------------------------------------------------------
# package: SysDefCollector
#
# usage: Interacts with the SysDefParser to obtain those parts of the system
#        definition which are relevant to building a named configuration within the
#        system definition. Contains a SysDefCollector::ParserClient instance which
#        acts as the interface to the SysDefParser. This separation reduces the
#        possibility of a method name clash due to the parser callback mechanism
#        requiring the client to implement methods of the same name as the XML
#        element tags of interest.
#
# public methods:
#
#    new(configname, loghandle): constructs a new instance to collect system 
#       definition info relating to the configuration name 'configname'.
#
#    parserClient(): returns a reference to the SysDefCollector::ParserClient
#       instance - typically for passing to the parser.
#
#    options(): returns a list of the abld options flags as specified in the
#       'option' elements.
#
#    targets(): returns a list of the abld target flags as specified by the
#       'targetList' attributes for each 'buildLayer' element in the specified
#       configuration.
#
#    specialInstructionsFlag(): returns true/false accordingly as any relevant
#       'specialInstructions' elements are present/not present. Relevant means
#       instructions which invoke SETUPPRJ.BAT.
#
#    components(): returns a hash of component name and bldFile directories
#        for each component to be built for the specified configuration.
#
#    dump(): debug/development method to dump the internal data structures
#
#    test(): debug/development method to dump the results of the methods
#        'options()', 'targets()', 'specialInstructionsFlag()', 'components()'.
#
#-------------------------------------------------------------------------------
package SysDefCollector;
use strict;

my $debugFlag = 0;

sub new 
{
    my ($class, $configname, $loghandle) = @_;
    my $self = { client => SysDefCollector::ParserClient->new($configname,$loghandle), loghandle => $loghandle };
    return bless $self, $class;
}

sub parserClient
{
    my $self = shift;
    return $self->{client};
}

#-------------------------------------------------------------------------------
# sub options() - returns the translated list of options for each 'option' element
#-------------------------------------------------------------------------------
sub options
{
    my $self = shift;
    return $self->_collectedList('option');
}

#-------------------------------------------------------------------------------
# sub targets() - returns the translated list of targets for each 'buildLayer'
#    in the named configuration.
#-------------------------------------------------------------------------------
sub targets
{
    my $self = shift;

    my @targets;
    my @buildLayerTargetList = $self->_collectedList('buildLayerTargetList');

    for my $layerTarget (@buildLayerTargetList)
    {
        my %targetListHash = $self->_collectedHash('targetList');
        my @targetList = @{ $targetListHash{$layerTarget} };
        push @targets, @targetList;
    }

    # eliminate any duplicates by storing as hash keys
    my %targetHash = map { $_, '' } @targets;

    # now translate via the target mapping
    my %targetMap = $self->_collectedHash('target');
    @targets = map { $targetMap{$_} } keys %targetHash;

    return @targets;
}

#-------------------------------------------------------------------------------
# sub specialInstructionsFlag() - returns true if 'specialInstructions' elements are present.
#-------------------------------------------------------------------------------
sub specialInstructionsFlag
{
    my $self = shift;
    my $flag = 0;
    $flag = $self->_collected()->{specialInstructions}
                        if exists $self->_collected()->{specialInstructions};
    return $flag;
}

#-------------------------------------------------------------------------------
# sub components() - returns an array of components to be built for the named
#    configuration. Each array element is a reference to a further array whose
#    element[0] is the component name and element[1] is the directory location
#    of that component's 'bld.inf' file.
#-------------------------------------------------------------------------------
sub components
{
    my $self = shift;
    my $loghandle = $self->{loghandle};
    
    my @unitNames;
    my @unitListRef = $self->_collectedList('unitListRef');
    my %unitList    = $self->_collectedHash('unitList');
    my %unitListNamesHash;  # Used to detect duplicates and then discarded!
    my %unitNamesHash;      # Used to detect duplicates and then discarded!
    my %unitMap = $self->_collectedHash('unit');
    
    for my $unitListName (@unitListRef)
    {
        if (defined $unitListNamesHash{$unitListName})
        {    # Duplicate unitListName! Ignore it!
            print $loghandle "Ignoring duplicated unitList: $unitListName\n";
            next;
        }
        $unitListNamesHash{$unitListName} = 1;
        unless (defined $unitList{$unitListName})
        {     # No info for this unitList!
            print $loghandle "No Unit info for unitList: $unitListName\n";
            next;
        }
        my @units = @{ $unitList{$unitListName} };
        foreach my $unit (@units)
        {
            if (defined $unitNamesHash{$unit})
            {    # Duplicate unit name! Ignore it!
                print $loghandle "Ignoring duplicated Unit: $unit\n";
                next;
            }
            $unitNamesHash{$unit} = 1;
            unless (defined $unitMap{$unit})
            {      # No bldFile (directory) info for this component!
                print $loghandle "No bldFile info for Unit: $unit\n";
                next;
            }
            my @unitdef = ($unit, $unitMap{$unit});
            push @unitNames, \@unitdef;
        }
    }

    return @unitNames;
}

#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------
sub dump
{
    my $self = shift;
    my $fh = shift;
    $self->parserClient($fh)->dump($fh);
}

#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------
sub test
{
    my $self = shift;
    my $fh = $self->{loghandle};    # Logfile handle

    my @options    = $self->options();
    my @targets    = $self->targets();
    my $special    = $self->specialInstructionsFlag();
    my @components = $self->components($fh);

    print $fh "\nTest Collected System Definition Query Methods\n";
    print $fh "==============================================\n";

    print $fh "options: ['", (join "', '", @options), "']\n";
    print $fh "targets: ['", (join "', '", @targets), "']\n";
    print $fh "special instructions: '", ($special ? "yes" : "no" ), "'\n";
    print $fh "components:\n{\n";
    for my $component (@components)
    {
        print $fh "\t'", $component->[0], "' => '", $component->[1], "'\n";
    }
    print $fh "}\n";
    print $fh "==============================================\n";
}

#-------------------------------------------------------------------------------
# private methods:
#-------------------------------------------------------------------------------
sub _collected
{
    my $self = shift;
    return $self->parserClient()->{collected};
}

sub _collectedHash
{
    my ($self, $slot) = @_;
    my %hash = ();
    %hash = %{ $self->_collected()->{$slot} }
                        if exists $self->_collected()->{$slot};
    return %hash;
}

sub _collectedList
{
    my ($self, $slot) = @_;
    my @list = ();
    @list = @{ $self->_collected()->{$slot} }
                        if exists $self->_collected()->{$slot};
    return @list;
}

#-------------------------------------------------------------------------------
# package: SysDefCollector::ParserClient
#
# usage: Interacts directly with the SysDefParser to obtain those parts of the system
#        definition which are of interest. Implements the parser callback methods
#        for the XML elements for which we collect information. Some elements are
#        of interest only if they are enclosed within an outer element with certain
#        properties. Other elements are always of interest. The latter style of
#        element is always collected. The former is only collected when it is known
#        that we are within an appropriate enclosing element. The 'context' property
#        is used for testing this condition.
#
# methods:
#
#    new(configname): constructs a new instance to collect system definition info
#       relating to the configuration name 'configname'.
#
#    parserClient(): returns a reference to the SysDefCollector::ParserClient
#       instance - typically for passing to the parser.
#
#-------------------------------------------------------------------------------
package SysDefCollector::ParserClient;
use strict;

sub new
{
    my ($class, $configname, $loghandle) = @_;
    my $self = { configname => $configname, configfound => 0, context => {intask => 0}, collected => {}, loghandle => $loghandle };
    return bless $self, $class;
}

#-------------------------------------------------------------------------------
# The following methods 'configuration()', 'configuration_()' initiate and
# terminate respectively the collection of element information found inside a
# 'configuration' element with 'name' attribute matching the objects 'configname'
# attribute.
#-------------------------------------------------------------------------------
sub configuration
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);
    my $loghandle = $self->{loghandle};

    # start of a 'configuration' element - if the name of the element matches our
    # 'configname' attribute then we create contexts so that elements of interest
    # nested within this 'configuration' element can be collected.
    unless ($attrs{name} eq $self->{configname}) { return; }
    
    if ($self->{configfound})
    {
        print $loghandle "Ignoring duplicated configuration: $attrs{name} ($attrs{description})\n";
    }
    else
    {
        $self->{configfound} = 1;
        $self->{context}->{unitListRef} = [];
        $self->{context}->{buildLayerTargetList} = [];
    }
}

sub configuration_
{
    my ($self, $expat, $element) = @_;
    $self->_debugout(@_);

    # end of a 'configuration' element - save what we have collected within this
    # 'configuration' element and delete the context so as to terminate collection
    # of any subsequently encountered nested elements.

    if (exists $self->{context}->{unitListRef})
    {
        $self->{collected}->{unitListRef} = $self->{context}->{unitListRef};
        delete $self->{context}->{unitListRef};
    }

    if (exists $self->{context}->{buildLayerTargetList})
    {
        # eliminate duplicates
        my %hash = map { $_, '' } @{$self->{context}->{buildLayerTargetList}};
        my @unique = keys %hash;
        $self->{collected}->{buildLayerTargetList} = \@unique;
        delete $self->{context}->{buildLayerTargetList};
    }
}

#-------------------------------------------------------------------------------
# Method 'unitListRef()' accumulates 'unitListRef' unitList information found
# within a 'configuration element with matching name.
#-------------------------------------------------------------------------------
sub unitListRef
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);
    
    if($self->{context}->{intask})
        { return; }     # Task-specific unitListRef not supported

    # if there is a previously created context for 'unitListRef's then store this one.

    if (exists $self->{context}->{unitListRef})
    {
        push @{$self->{context}->{unitListRef}}, $attrs{unitList};
    }
    my $x = 1;
}

#-------------------------------------------------------------------------------
# Methods 'task()' and 'task_()' track context (i.e. inside a task or not)
# because task-specific activities are not supported.
#-------------------------------------------------------------------------------
sub task
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);
    $self->{context}->{intask} = 1;
}

sub task_
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugout(@_);
    $self->{context}->{intask} = 0;
}

#-------------------------------------------------------------------------------
# Method 'buildlayer()' accumulates 'buildlayer' targetList information found
# within a 'configuration element with matching name.
#-------------------------------------------------------------------------------
sub buildLayer
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    if (exists $self->{context}->{buildLayerTargetList})
    {
        push @{$self->{context}->{buildLayerTargetList}}, (split /\s+/, $attrs{targetList});
    }
}

#-------------------------------------------------------------------------------
# The following three methods 'unitList()', 'unitList_()' and 'unitRef()'
# accumulate 'unitList' and 'unitRef' information found within the 'build' elements.
#-------------------------------------------------------------------------------
sub unitList
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    # start of a 'unitList' element  - create a context so that collection of all
    # 'unitRef's elements found within this 'unitList' element can be collected.

    die "Fatal: context already has unitList\n" if exists $self->{context}->{unitList};
    $self->{context}->{unitList} = { name => $attrs{name}, list => [] };
}

sub unitList_
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugout(@_);

    # end of the current 'unitList' element - save what we have collected
    # and delete the context

    $self->{collected}->{unitList} = {} if ! exists $self->{collected}->{unitList};

    my $unitList = delete $self->{context}->{unitList};
    $self->{collected}->{unitList}->{$unitList->{name}} = $unitList->{list};

}

sub unitRef
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    # unitRef found - save unitRef data to current context

    die "Fatal: context requires unitList\n" if ! exists $self->{context}->{unitList};
    push @{$self->{context}->{unitList}->{list}}, $attrs{unit};
}

#-------------------------------------------------------------------------------
# The method 'unit()' accumulates 'unit' information found within the 'systemModel'
# elements.
#-------------------------------------------------------------------------------
sub unit
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    # no need to set up a temporary context to collect these since they have global scope
    $self->{collected}->{unit} = {} if ! exists $self->{collected}->{unit};
    $self->{collected}->{unit}->{$attrs{unitID}} = $attrs{bldFile};
}

#-------------------------------------------------------------------------------
# sub option() - accumulates 'option' element information found within the
# 'build' element.
#-------------------------------------------------------------------------------
sub option
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    if ($attrs{enable} =~ /[Yy]/)
    {
        # no need to set up a temporary context to collect these since they have global scope
        $self->{collected}->{option} = [] if ! exists $self->{collected}->{option};
        push @{$self->{collected}->{option}}, $attrs{abldOption};
    }
}

#-------------------------------------------------------------------------------
# sub target() - accumulates 'target' element information found within the
# 'build' element.
#-------------------------------------------------------------------------------
sub target
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    $self->{collected}->{target} = {} if ! exists $self->{collected}->{target};
    $self->{collected}->{target}->{$attrs{name}} = $attrs{abldTarget};
}

#-------------------------------------------------------------------------------
# sub targetList() - accumulates 'targetList' element information found within the
# 'build' element.
#-------------------------------------------------------------------------------
sub targetList
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);

    $self->{collected}->{targetList} = {} if ! exists $self->{collected}->{targetList};
    my @list = split /\s+/, $attrs{target};
    $self->{collected}->{targetList}->{$attrs{name}} = \@list;
}

#-------------------------------------------------------------------------------
# sub specialInstructions() - sets the 'specialInstructions' flag if a
# 'specialInstructions' element is encountered. In practice, we are only
# interested in instructions which invoke SETUPPRJ.BAT as this will require
# the inclusion of the "bootstrap" line in the output text file.
#-------------------------------------------------------------------------------
sub specialInstructions
{
    my ($self, $expat, $element, %attrs) = @_;
    $self->_debugin(@_);
    if ($attrs{command} =~ /^setupprj.bat/i)
        {
        $self->{collected}->{specialInstructions} = 1;
        }
}

#-------------------------------------------------------------------------------
# utility routines for development/debug purposes.
#-------------------------------------------------------------------------------

sub _debugin
{
##    return;             ## Suppress this debugging!
    my $self = shift;
    my ($ignore0, $ignore2, $element, @args) = @_;
    my $loghandle = $self->{loghandle};
    if ($debugFlag) { print $loghandle "Enter: $element (", (join ' ', @args), ")\n"; }
}

sub _debugout
{
##    return;             ## Suppress this debugging!
    my $self = shift;
    my $loghandle = $self->{loghandle};
    if ($debugFlag) { print $loghandle "Leave: $_[2]\n"; }
}

sub dump
{
    my $self = shift;
    my $fh = shift;

    print $fh "\nDump Collected System Definition\n\n";    
    print $fh "================================\n";    

    if (keys %{$self->{collected}} > 0)
    {
        if (exists $self->{collected}->{option})
        {
            my @option = @{$self->{collected}->{option}};
            print $fh "option :[", (join ',', @option), "]\n";
        }

        if (exists $self->{collected}->{specialInstructions})
        {
            my $flag = $self->{collected}->{specialInstructions};
            print $fh "specialInstructions : '", ($flag ?  "yes" : "no"), "'\n";
        }
        else
        {
            print $fh "specialInstructions : 'no'\n";
        }

        if (exists $self->{collected}->{buildLayerTargetList})
        {
            my @buildLayerTargetList = @{$self->{collected}->{buildLayerTargetList}};
            print $fh "buildLayerTargetList :[", (join ',', @buildLayerTargetList), "]\n";
        }

        if (exists $self->{collected}->{unitListRef})
        {
            my @unitListRef = @{$self->{collected}->{unitListRef}};
            print $fh "unitListRef :[", (join ',', @unitListRef), "]\n";
        }

        if (exists $self->{collected}->{unitList})
        {
            print $fh "unitList:\n{\n";
            my %unitList = %{$self->{collected}->{unitList}};
            for my $key (keys %unitList)
            {
                 my @list = @{$unitList{$key}};
                 print $fh "\t'$key' has units:[", (join ',', @list), "]\n";
            }
            print $fh "}\n";
        }

        if (exists $self->{collected}->{target})
        {
            print $fh "target:\n{\n";
            my %target = %{$self->{collected}->{target}};
            for my $key (keys %target)
            {
                 print $fh "\t'$key' => '", $target{$key} , "'\n";
            }
            print $fh "}\n";
        }

        if (exists $self->{collected}->{targetList})
        {
            print $fh "targetList:\n{\n";
            my %targetList = %{$self->{collected}->{targetList}};
            for my $key (keys %targetList)
            {
                 my @list = @{$targetList{$key}};
                 print $fh "\t'$key' has targets:[", (join ',', @list), "]\n";
            }
            print $fh "}\n";
        }

        if (exists $self->{collected}->{unit})
        {
            print $fh "unit:\n{\n";
            my %unit = %{$self->{collected}->{unit}};
            for my $key (keys %unit)
            {
                 print $fh "\t'$key' => '", $unit{$key} , "'\n";
            }
            print $fh "}\n";
        }
    }
    else
    {
        print $fh "Nothing collected\n";
    }
    print $fh "================================\n";    
}

#-------------------------------------------------------------------------------
# -EOF-
#-------------------------------------------------------------------------------
1;