########################################################################
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
#
# Module name   : GCC.PL
# Module type   : JATS Utility
# Compiler(s)   : Perl
# Environment(s): jats
#
# Description   : GCC C/C++ toolset
#
#......................................................................#

use strict;
use warnings;

# Global variables
our $ScmPlatform;
our $ScmNoToolsTest;

#   Misc variables
my $UseGcov = 0;
my $UseProfile = 0;
my $LcovExplicitBranch = '';
my $UseCppcheck = 0;
my $GCCRPath;
my @ldFixups;

##############################################################################
#   Configuration information
#   Cross reference from CrossCompiler Alias to actual paths
#   Structure:
#   Hash reference: Array of toolchain information
#       Mandatory
#          ROOT             => Root of compiler
#          BASE             => Base for binaries
#       Optional
#          CC_OPTS          => Additonal Compiler options
#          CC_OPTSP         => Production Compiler options
#          CC_OPTSD         => Debug Compiler options
#          CXX_OPTS         => Additional C++ Options
#          LDFLAGS          => Linker flags
#          UNCONTROLLED     => Boolean to create warning
#          DEPRECATED       => Boolean to create warning
#          DISCONTINUED     => Boolean to create error and not build anything
#          PACKAGE_ARCH     => Packageing architecture
#          VERSION          => Version reported by GCC
#          MACHINE          => Machine reported by GCC
#          RPATH            => Relative Path to compiler installed runtime
#                              Absolute or Relative
#          COMPILER_OPTIONS => Toolset specific compiler initial options
#                              This is a comma seperated list
my %ToolsetConfig = (

    'i386-unknown-linux-gnu'    => {
        ROOT        => '/opt/crosstool/gcc-4.1.1-glibc-2.5/i586-unknown-linux-gnu',
        BASE        => 'bin/i586-unknown-linux-gnu-',
       VERSION      => '4.1.1',
       MACHINE      => 'i586-unknown-linux-gnu'
    },

    'arm-9tdmi-linux-gnu'       => {
        ROOT        => '/opt/crosstool/gcc-4.1.1-glibc-2.5/arm-9tdmi-linux-gnu',
        BASE        => 'bin/arm-9tdmi-linux-gnu-',
        VERSION     => '4.1.1',
        MACHINE     => 'arm-9tdmi-linux-gnu'
        },

    'powerpc-603e-linux-gnu'    => {
       ROOT         => '/opt/crosstool/gcc-4.1.1-glibc-2.5/powerpc-603e-linux-gnu',
       BASE         => 'bin/powerpc-603e-linux-gnu-',
       VERSION      => '4.1.1',
       MACHINE      => 'powerpc-603e-linux-gnu'
    },

    'arm-926ejs-linux-gnueabi'  => {
       ROOT         => '/opt/crosstool/gcc-4.4.3-glibc-2.9/arm-926ejs-linux-gnueabi',
       BASE         => 'bin/arm-926ejs-linux-gnueabi-',
       CC_OPTS      => '-Wno-psabi',
       VERSION      => '4.4.3',
       MACHINE      => 'arm-926ejs-linux-gnueabi'
    },

    'arm-926ejs-linux-gnueabi-v2'  => {
       ROOT         => '/opt/crosstool/gcc-5.2.0-glibc-2.17/arm-926ejs-linux-gnueabi',
       BASE         => 'bin/arm-926ejs-linux-gnueabi-',
       CC_OPTS      => '-Wno-psabi',
       CXX_OPTS     => '-std=gnu++11',
       VERSION      => '5.2.0',
       MACHINE      => 'arm-926ejs-linux-gnueabi'
    },

    'powerpc-603e-linux-gnu-v2'  => {
       ROOT         => '/opt/crosstool/gcc-5.2.0-glibc-2.17/powerpc-603e-linux-gnu',
       BASE         => 'bin/powerpc-603e-linux-gnu-',
       CXX_OPTS     => '-std=gnu++11',
       VERSION      => '5.2.0',
       MACHINE      => 'powerpc-603e-linux-gnu'
    },

    'i686-linux-gnu'    => {
       ROOT         => '/usr',
       BASE         => 'bin/',
       PACKAGE_ARCH => 'i386',
       VERSION      => '4.6',
       MACHINE      => 'i686-linux-gnu'
    },

    'i686-vix_ubuntu12_c11-linux-gnu'    => {
       ROOT         => '/opt/crosstool/gcc-5.2.0-glibc-2.17/i686-vix_ubuntu12_c11-linux-gnu',
       BASE         => 'bin/i686-vix_ubuntu12_c11-linux-gnu-',
       PACKAGE_ARCH => 'i386',
       CXX_OPTS     => '-std=gnu++11',
       VERSION      => '5.2.0',
       MACHINE      => 'i686-vix_ubuntu12_c11-linux-gnu',
       RPATH        => '/lib/i686-vix_ubuntu12_c11-linux-gnu',
    },

    'arm-iwmmxt-linux-gnueabi'  => {
       ROOT         => '/opt/marvel',
       BASE         => 'bin/arm-iwmmxt-linux-gnueabi-',
       VERSION      => '4.2.4',
       MACHINE      => 'arm-iwmmxt-linux-gnueabi'
    },

    'arm-unknown-linux-gnueabi-sk20-4.1.1.0'  => {
       ROOT         => '/opt/arm-unknown-linux-gnueabi-sk20-4.1.1.0',
       BASE         => 'bin/arm-unknown-linux-gnueabi-',
       VERSION      => '4.8.2',
       MACHINE      => 'arm-unknown-linux-gnueabi'
    },

    'gmx-sdk-imx6-toolchain-5.0.0.0'  => {
       ROOT         => '/opt/gmx-sdk-imx6-toolchain-5.0.0.0',
       BASE         => 'bin/arm-unknown-linux-gnueabi-',
       VERSION      => '4.9.1',
       MACHINE      => 'arm-unknown-linux-gnueabi',
       COMPILER_OPTIONS =>  'pic,brokenldscripts',
    },

    # Ubuntu14
    'x86_64-linux-gnu'    => {
       ROOT         => '/usr',
       BASE         => 'bin/',
       PACKAGE_ARCH => 'amd64',
       CXX_OPTS     => '-std=gnu++11',
       VERSION      => '4.8',
       MACHINE      => 'x86_64-linux-gnu',
       COMPILER_OPTIONS =>  'pic'               # Always create PIC
    },

    # Ubuntu16
    'x86_64-linux-gnu-5.4'    => {
       ROOT         => '/usr',
       BASE         => 'bin/',
       PACKAGE_ARCH => 'amd64',
       CXX_OPTS     => '-std=gnu++11',
       VERSION      => '5.4.0',
       MACHINE      => 'x86_64-linux-gnu',
       COMPILER_OPTIONS =>  'pic'               # Always create PIC
    },

    'x86_64-redhat-linux-4.8.5' => {
        ROOT         => '/usr',
        BASE         => 'bin/',
        PACKAGE_ARCH => 'x86_64',
        CXX_OPTS     => '-std=gnu++11',
        VERSION      => '4.8.5',
        MACHINE      => 'x86_64-redhat-linux',
        COMPILER_OPTIONS =>  'pic'               # Always create PIC
        },

    # STIB Parkeon
    'arm-linux-gnueabi-4.6.3' => {
        ROOT         => '/opt/parkeon-arm-linux-gnueabi-4.6',
        BASE         => 'bin/arm-linux-gnueabi-',
        PACKAGE_ARCH => 'armel',
        #CXX_OPTS     => '',
        VERSION      => '4.6.3',
        MACHINE      => 'arm-linux-gnueabi',
        COMPILER_OPTIONS =>  'pic'               # Always create PIC
        },


    #
    #   Old (not to be used) version of the embedded toolchain
    #   This was deprecated in favor of gcc-4.1.1-glibc-2.5
    #   It is not possible to reproduce old packages using the old compiler
    #   This is a known issue
    #
    'i386-unknown-linux-gnu.glibc-2.3.2' => {
       ROOT         => '/opt/crosstool/gcc-4.1.0-glibc-2.3.2/i386-unknown-linux-gnu',
       BASE         => 'bin/i386-unknown-linux-gnu-'
    },

    'arm-9tdmi-linux-gnu.glibc-2.3.2' => {
       ROOT         => '/opt/crosstool/gcc-4.1.0-glibc-2.3.2/arm-9tdmi-linux-gnu',
       BASE         => 'bin/arm-9tdmi-linux-gnu-'
    },

    #
    #   Not too sure where this is used
    #
    'armv4l-unknown-linux-gcc' => {
       ROOT         => '/opt/host/armv4l',
       BASE         => 'bin/armv4l-unknown-linux-'
    },

    #
    #   The compiler for the current local machine
    #
    'i386-unknown-linux-gcc' => {
       ROOT         => '/usr',
       BASE         => 'bin/',
       UNCONTROLLED => 1,
       PACKAGE_ARCH => 'i386',
    },
);

#
#   Cross reference from GCCTarget to GBE_MACHTYPE for which it can
#   build using the 'native gcc'. This is NOT the preferred mode of operation
#   as the compiler is not as controlled as the cross compilers.
#
my %NativeCompilers = (
    'Linux i386'       => 'linux_i386',
    );

##############################################################################
#   ToolsetInit()
#       Runtime initialisation
#
##############################################################################

ToolsetInit();

sub ToolsetInit
{
    my( $GCCTarget, $GCCToolchain, $GCCRoot, $GCCBin, $GCCAr, $GCCObjCopy, $GCCGcovTool );
    my( $GCCFlags,  $GCCFlagsP, $GCCFlagsD, $LDFlags );
    my( $GCCXXFlags );
    my( $PkgArch);
    my( $arg_alias, $tools_found, $compiler_tool );

#.. Toolset configuration
#   Also $::ScmToolsetProperties{}
#
    $::ScmToolsetVersion = "1.0.0";             # our version
    $::ScmToolsetGenerate = 0;                  # GEN generate optional
    $::ScmToolsetCompilerPath = 1;              # Exports Compiler path to makefile via SCM_COMPILERPATH
    $::ScmToolsetProgDependancies = 0;          # handle Prog dependancies myself
    $::ScmToolsetSoName = 1;                    # Shared library supports SoName

#.. Standard.rul requirements
#
    $::s = "asm";
    $::o = "o";
    $::so = "so";
    $::a = "a";
    $::exe = "";

#.. Parse arguments
#
    foreach $_ ( @::ScmToolsetArgs ) {
        if (/^--Target=(.*)/) {                 # OS Version
            $GCCTarget = "$1";

        } elsif (/^--CrossAlias=(.*)/) {        # CrossCompiler target-alias
            $arg_alias = $1;

        } elsif (/^--UseGcov/) {                # Compile for code coverage
            $UseGcov = 1;

        } elsif (/^--UseProfile/) {             # Compile for profiling
            $UseProfile = 1;

        } elsif (/^--LcovExplicitBranch/) {     # Version of lcov requires explicit branch coverage
            $LcovExplicitBranch = ' --rc lcov_branch_coverage=1';

        } elsif (/^--UseCppcheck/) {            # Use cppcheck as the lint tool
            $UseCppcheck = 1;

        } elsif (/^--CompilerTool=(.*)/) {      # CrossCompiler located in package
            $compiler_tool = $1;

        } else {
            Message( "gcc toolset: unknown option $_ -- ignored\n" );
        }
    }

    foreach $_ ( @::ScmPlatformArgs ) {
        if (/^--product=(.*)/) {                # GBE product

        } else {
            Message( "gcc toolset: unknown platform argument $_ -- ignored\n" );
        }
    }

    Error ("TOOLSET/gcc - Target undefined" )
        unless ($GCCTarget);

    #
    #   If the toolset is not required, then do not process any futher
    #   We may not find the compiler
    #
    return
        if ($ScmNoToolsTest);

#.. Cross compile support
#   Compiler provided in package, rather than install on machine
#
if ( $compiler_tool  )
{
    #
    #   The GCC toolset will handle a compiler provided within a package
    #   Initial requirement was for ANDROID NDKs where the compiler is
    #   a part of the NDK and will change.
    #
    #   Compilers in packages will have a file in gbe/COMPILERS/<compiler_tool>
    #   that contains data specifically designed for this toolset
    #
    Verbose("Locate compiler in package: $compiler_tool");
    my @toolList;
    my $toolPath;
    my $toolPkg;
    for my $entry (@{$::ScmBuildPkgRules{$ScmPlatform} })
    {
        my $tpath = join('/', $entry->{ROOT}, 'gbe', 'COMPILERS' , $compiler_tool);
        if ( -f $tpath  )
        {
            push @toolList, $entry->{NAME};
            $toolPath = $tpath;
            $toolPkg = $entry;
        }
    }
    Error ("Multiple packages provide required compiler:", @toolList)
        if ( scalar(@toolList) > 1 );
    Error ("Required compiler not found in any package", "Compiler: $compiler_tool")
        unless ( scalar(@toolList) == 1 );

    #
    #   Process the compiler info file
    #   Rip out the data and create a hash of item/values
    #   File format:
    #       '#' is a line comment
    #       item=value
    #
    my %data;
    open (my $DATA, '<', $toolPath ) || Error("Cannot open compiler datafile. $!", "File: $toolPath");
    while ( <$DATA> )
    {
        $_ =~ s~\s+$~~;
        next if ( m~^#~ );
        m~(.*?)\s*=\s*(.*)~;
        $data{$1} = $2;
    }
    close $DATA;

    #
    #   Force this compilers data into the ToolsetConfig hash
    #
    $arg_alias = $compiler_tool;
    %ToolsetConfig = ();

    $ToolsetConfig{$arg_alias}{ROOT} = join('/', $toolPkg->{ROOT}, $data{ROOT} );
    $ToolsetConfig{$arg_alias}{BASE} = $data{BASE} . '-';
    $ToolsetConfig{$arg_alias}{CC_OPTS} = $data{CFLAGS};
    $ToolsetConfig{$arg_alias}{CC_OPTSP} = $data{CFLAGSP};
    $ToolsetConfig{$arg_alias}{CC_OPTSD} = $data{CFLAGSD};
    $ToolsetConfig{$arg_alias}{CXX_OPTS} = $data{CXXFLAGS};
    $ToolsetConfig{$arg_alias}{LDFLAGS} = $data{LDFLAGS};
    $ToolsetConfig{$arg_alias}{VERSION} = $data{VERSION};
    $ToolsetConfig{$arg_alias}{MACHINE} = $data{MACHINE};
    $ToolsetConfig{$arg_alias}{COMPILER_OPTIONS} = $data{COMPILER_OPTIONS};
    $ToolsetConfig{$arg_alias}{RAPTH} = $data{RPATH};
    $ToolsetConfig{$arg_alias}{DEPRECATED} = $data{DEPRECATED};
    $ToolsetConfig{$arg_alias}{DISCONTINUED} = $data{DISCONTINUED};
}

#.. Cross compile support
#
#   Toolchain=root,[bin]
#
    if ( $arg_alias )
    {
        if ( exists $ToolsetConfig{ $arg_alias } )
        {
            $GCCToolchain = $ToolsetConfig{ $arg_alias };
            my $testCompilerPath = $GCCToolchain->{ROOT} . '/' . $GCCToolchain->{BASE} . 'gcc';
            $tools_found = (-d $GCCToolchain->{ROOT}) && ( -f $testCompilerPath);
            Warning ("gcc toolset: CrossPlatform toolchain not found for: $arg_alias",
                     "Path    : $GCCToolchain->{ROOT}" ,
                     "Compiler: $testCompilerPath "
                      ) unless $tools_found;
        }
        else
        {
            Error("gcc toolset: CrossPlatform Alias not configured: $arg_alias");
        }

        Warning ("Uncontrolled toolchain used: $arg_alias")
            if ( exists($GCCToolchain->{UNCONTROLLED}) && $GCCToolchain->{UNCONTROLLED} );

        Warning ("Deprecated toolchain used: $arg_alias")
            if ( exists($GCCToolchain->{DEPRECATED}) && $GCCToolchain->{DEPRECATED} );

        Error ("Discontinued toolchain used: $arg_alias")
            if ( exists($GCCToolchain->{DISCONTINUED}) && $GCCToolchain->{DISCONTINUED} );
    }

    #
    #   If no Cross compiler toolchain is found (preferred method), then attempt
    #   to match a native compiler. Only known targets allow a native build
    #
    unless ( $tools_found )
    {
        if ( exists ( $NativeCompilers{$GCCTarget} ))
        {
            if ( $NativeCompilers{$GCCTarget} eq $::GBE_MACHTYPE )
            {
                $tools_found = 1;
                $GCCToolchain = undef;
            }
        }
    }

    #
    #   Must have a toolset by now, either a cross compiler or Native
    #
    Error ("gcc toolset: Toolchain not found for: $GCCTarget" )
        unless ( $tools_found );


    if ( defined $GCCToolchain )
    {
        #
        #   Parse GCCToolchain. Potential parts to create
        #       GCCRoot     - Location to find the effective /usr/include directory
        #       GCCBin      - Path to the gcc executable
        #       GCCAr       - Path to the ar executable
        #       GCCFlags    - Additional compiler flags. Also Production and Debug
        #
        $GCCRoot = $GCCToolchain->{ROOT};
        $GCCBin = '${GCC_ROOT}/' . $GCCToolchain->{BASE} . 'gcc';
        $GCCAr =  '${GCC_ROOT}/' . $GCCToolchain->{BASE} . 'ar';
        $GCCGcovTool = '${GCC_ROOT}/' . $GCCToolchain->{BASE} . 'gcov';
        $GCCObjCopy =  '${GCC_ROOT}/' . $GCCToolchain->{BASE} . 'objcopy';
        if (exists $GCCToolchain->{RPATH}) {
            if( $GCCToolchain->{RPATH} =~ m~^/~ ) {
                $GCCRPath = $GCCToolchain->{RPATH};
            } else {
                $GCCRPath = '${GCC_ROOT}/' . $GCCToolchain->{RPATH};
            }
        }
        $GCCFlags = $GCCToolchain->{CC_OPTS};
        $GCCFlagsP = $GCCToolchain->{CC_OPTSP};
        $GCCFlagsD = $GCCToolchain->{CC_OPTSD};
        $GCCXXFlags = $GCCToolchain->{CXX_OPTS};
        $LDFlags = $GCCToolchain->{LDFLAGS} if defined($GCCToolchain->{LDFLAGS});
        $PkgArch = $GCCToolchain->{PACKAGE_ARCH};
        SetGlobalOption('PACKAGE_ARCH', $PkgArch) if defined $PkgArch;
    }
    else
    {
        $GCCRoot = '/usr';
        $GCCBin = 'gcc';
        $GCCAr = 'ar';
        $GCCObjCopy =  'objcopy';
    }

    #
    #   When running under gcov we need to instruct GCC to perform code coverage
    #   generation in both C flags and LD flags
    #
    if ( $UseGcov )
    {
        $GCCFlags .= ' -coverage';
        $LDFlags  .= ' -coverage';
    }

    #
    #   When running with cppcheck we need to include it in our environment
    #
    if ( $UseCppcheck )
    {
        ToolsetRequire( "cppcheck" );
        PlatformDefine( "CPPCHECK_PLATFORM := unix32" );
    }


#.. Define GCC environment
#
    PlatformDefine( "
#################################################
# GCC toolchain definitions
#
#..");
    PlatformDefine( "GCC_TARGET         := $GCCTarget" );
    PlatformDefine( "GCC_ROOT           := $GCCRoot" );
    PlatformDefine( "GCC_CC             := $GCCBin" );
    PlatformDefine( "GCC_AR             := $GCCAr" );
    PlatformDefine( "GCC_OBJCOPY        := $GCCObjCopy" );
    PlatformDefine( "GCC_GCOVTOOL       := $GCCGcovTool" );
    PlatformDefine( "GCC_CFLAGS         := $GCCFlags" ) if defined $GCCFlags;
    PlatformDefine( "GCC_CFLAGSP        := $GCCFlagsP" ) if defined $GCCFlagsP;
    PlatformDefine( "GCC_CFLAGSD        := $GCCFlagsD" ) if defined $GCCFlagsD;
    PlatformDefine( "GCC_LDFLAGS        := $LDFlags" ) if defined $LDFlags;
    PlatformDefine( "GCC_CXXFLAGS       := $GCCXXFlags" ) if defined $GCCXXFlags;


    #
    #   Required since this toolset advertises: ScmToolsetCompilerPath
    #
    PlatformDefine( "SCM_COMPILERPATH   := \$\{GCC_CC\}" );

    #
    #   Sanity checking
    #
    PlatformDefine( "GCC_EVERSION       := " . $GCCToolchain->{VERSION} ) if defined $GCCToolchain->{VERSION};
    PlatformDefine( "GCC_EMACHINE       := " . $GCCToolchain->{MACHINE} ) if defined $GCCToolchain->{MACHINE};

    #
    #   Option indication of packaging architecture
    #   Used by non-embedded systems for packaging. See debian_packager
    #
    PlatformDefine( "PACKAGE_ARCH       := $PkgArch" ) if (defined $PkgArch);
    PlatformDefine( "" );

#.. Piece the world together
#
    Init( 'gcc' );
    ToolsetDefines( "gcc.def" );
    ToolsetRules( "gcc.rul" );
    ToolsetRules( "standard.rul" );


    PlatformDefine( "CTAGS_EXE:= ctags" );
    ToolsetRequire( "exctags" );                # and Exuberant Ctags

#   Create a standard data structure
#   This is a hash of hashes
#       The first hash is keyed by CompileOption keyword
#       The second hash contains pairs of values to set or remove
#
    %::ScmToolsetCompilerOptions =
    (
        #
        #   Control the thread model to use
        #   This will affect the compiler options and the linker options
        #
        'staticprogs'        => { 'STATIC_PROGS' , '1' },      # Progams link staticlly
        'no_staticprogs'     => { 'STATIC_PROGS' , undef },    # Default
        'noversiondll'       => { 'NO_VERSIONED_DLLS', 1 },    # Matches usage elsewhere
        'pic'                => { 'GEN_PIC' , '1' },           # Force PIC for static libs
        'nopic'              => { 'GEN_PIC' , undef },         # No Pic on Static libs
        'brokenldscripts'    => { 'BROKEN_LDSCRIPTS' , 1 },    # the linker is broken and cannot handle linker scripts
    );

    #
    #   Set default options
    #
    $::ScmCompilerOpts{'STATIC_PROGS'} = undef;
    $::ScmCompilerOpts{'NO_VERSIONED_DLLS'} = undef;
    $::ScmCompilerOpts{'USE_PROFILE'} = 1 if $UseProfile;

    #
    #   Process toolset-specfic compiler options
    #
    if ( exists $GCCToolchain->{COMPILER_OPTIONS} )
    {
        CompileOptions('*', split(',',$GCCToolchain->{COMPILER_OPTIONS}) );
    }
}

###############################################################################
#   ToolsetCTAGS()
#       This subroutine takes the user options and builds the rules
#       required to build the CTAGS database.
#
#   Arguments:
#       --xxx                   No arguments currently defined
#
#   Output:
#       [ctags:]
#           $(EXCTAGS)
#
###############################################################################

sub ToolsetCTAGS
{
    EXCTAGS( @_ ) unless $ScmNoToolsTest;
}


###############################################################################
#   ToolsetCC( $source, $obj, \@args )
#       This subroutine takes the user options and builds the rule(s)
#       required to compile the source file 'source' to 'obj'
#
###############################################################################

sub ToolsetCC
{
    my( $source, $obj, $pArgs ) = @_;
    my( $cflags, $file ) = "";

    foreach $_ ( @$pArgs ) {
        if (/--Shared$/) {                      # Building a 'shared' object
            $cflags  = "$cflags \$(SHCFLAGS)";
        } else {
            Message( "CC: unknown option $_ -- ignored\n" );
        }
    }

    MakePrint( "\n\t\$(CC)\n" );
    if ( $cflags )
    {                                           # object specific CFLAGS
        MakePadded( 4, "\$(OBJDIR)/$obj.$::o:" );
        MakePrint( "\tCFLAGS +=$cflags\n" );
    }

    $file = StripExt( $obj );                   # Metric working file
    ToolsetGenerate( "\$(OBJDIR)/$file.met" );

    if ( $UseGcov )
    {
        ToolsetGenerate( '$(OBJDIR)/' . $file . '.gcno' );
        ToolsetGenerate( '$(OBJDIR)/' . $file . '.gcda' );
    }
}


###############################################################################
#   ToolsetCCDepend( $depend, \@sources )
#       This subroutine takes the user options and builds the
#       rule(s) required to build the dependencies for the source
#       files 'sources' to 'depend'.
#
###############################################################################

sub ToolsetCCDepend
{
    MakePrint( "\t\$(CCDEPEND)\n" );
}


###############################################################################
#   ToolsetCXX( $source, $obj, \@args )
#       This subroutine takes the user options and builds the rule(s)
#       required to compile the source file 'source' to 'obj'
#
###############################################################################

sub ToolsetCXX
{
    my( $source, $obj, $pArgs ) = @_;
    my( $cflags, $file ) = "";

    foreach $_ ( @$pArgs ) {
        if (/--Shared$/) {                      # Building a 'shared' object
            $cflags  = "$cflags \$(SHCXXFLAGS)";
        } else {
            Message( "CXX: unknown option $_ -- ignored\n" );
        }
    }

    MakePrint( "\n\t\$(CXX)\n" );
    if ( $cflags )
    {                                           # object specific CFLAGS
        MakePadded( 4, "\$(OBJDIR)/$obj.$::o:" );
        MakePrint( "\tCXXFLAGS +=$cflags\n" );
    }

    $file = StripExt( $obj );                   # Metric working file
    ToolsetGenerate( "\$(OBJDIR)/$file.met" );

    if ( $UseGcov )
    {
        ToolsetGenerate( '$(OBJDIR)/' . $file . '.gcno' );
        ToolsetGenerate( '$(OBJDIR)/' . $file . '.gcda' );
    }
}


###############################################################################
#   ToolsetCXXDepend( $depend, \@sources )
#       This subroutine takes the user options and builds the
#       rule(s) required to build the dependencies for the source
#       files 'sources' to 'depend'.
#
###############################################################################

sub ToolsetCXXDepend
{
    ToolsetCCDepend();
}


###############################################################################
#   ToolsetAS( $source, $obj, \@args )
#       This subroutine takes the user options and builds the rule(s)
#       required to compile the source file 'source' to 'obj'
#
###############################################################################

sub ToolsetAS
{
    MakePrint( "\n\t\$(AS)\n" );
}

sub ToolsetASDepend
{
}


###############################################################################
#   ToolsetAR( $name, \@args, \@objs )
#       This subroutine takes the user options and builds the rules
#       required to build the library 'name'.
#
#   Arguments:
#       n/a
#
#   Output:
#       $(BINDIR)/name$.${a}:   .... ]
#           $(AR)
#
#       name_ld += ...  Linker command file
#           :
#
#       name_dp += ...  Dependency list
#           :
#
###############################################################################

sub ToolsetAR
{
    my( $name, $pArgs, $pObjs ) = @_;

#.. Parse arguments
#
    foreach $_ ( @$pArgs )
    {
        Message( "AR: unknown option $_ -- ignored\n" );
    }

#.. Target
#
    MakePrint( "#.. Library ($name)\n\n" );     # label

    MakeEntry( "\$(LIBDIR)/$name\$(GBE_TYPE).$::a:\t",
        "", "\\\n\t\t", ".$::o", @$pObjs );

#.. Build library rule (just append to standard rule)
#
    MakePrint( "\n\t\$(AR)\n\n" );
}


###############################################################################
#   ToolsetARMerge( $name, \@args, \@libs )
#       This subroutine takes the user options and builds the rules
#       required to build the library 'name' by merging the specified
#       libaries
#
#   Arguments:
#       --xxx                   No arguments currently defined
#
#   Output:
#       [ $(LIBDIR)/name$.${a}:   .... ]
#           ...
#
###############################################################################

sub ToolsetARMerge
{
    MakePrint( "\n\t\$(ARMERGE)\n\n" );
}


###############################################################################
#   ToolsetSHLD( $name, \@args, \@objs, \@libraries, $ver )
#       This subroutine takes the user options and builds the rules
#       required to link the Shared Library 'name'.
#
#   Arguments:
#   n/a
#
#   Output:
#       $(LIBDIR)/name:     $(LIBDIR)/shared
#           ln -s $shared $name
#
#       $(LIBDIR)/name.dep: $(GBE_OBJDIR)
#       $(LIBDIR)/name.dep: $(GBE_LIBDIR)
#       $(LIBDIR)/name.dep: $(GBE_PLATFORM).mk
#           $(SHLDDEPEND)
#
#       $(LIBDIR)/shared:   SHNAME=name
#       $(LIBDIR)/shared:   SHBASE=base
#       $(LIBDIR)/shared:   $(LIBDIR)/name.dep  \
#           $(OBJECTS)
#
#       ifneq "$(findstring $(IFLAG),23)" ""
#       -include            "$(LIBDIR)/name.dep"
#       endif
#
#       name_ld += ...
#           :
#
###############################################################################

sub ToolsetSHLD
{
    my( $name, $pArgs, $pObjs, $pLibs, $ver ) = @_;
    my( $linkname, $soname, $shared, $dbgname, @preReqFiles, @scripts, $multi_scan );
    my $sosuffix;
    my $noVersionedLib = $::ScmCompilerOpts{'NO_VERSIONED_DLLS'};
    my $brokenLd = $::ScmCompilerOpts{'BROKEN_LDSCRIPTS'};
    my $isNodeFile = 0;

#.. Parse arguments
#
    foreach $_ ( @$pArgs )
    {
        if (/^--Def=(.*?)(\,(.*))?$/) {         # Library definition
            #
            #   Locate the Def file.
            #   If it is a generate file so it will be in the SRCS hash
            #   Otherwise the user will have to use Src to locate the file
            #
            my $def = MakeSrcResolve($1);

            if ($brokenLd) {
                #   The linker cannot handle the linker script
                #   The work around is to split the script into two that it can handle
                #   Only works with a Linker Script with VERSION and EXTERN entries
                my $index = scalar @ldFixups;
                my %data;
                $data{src} = $def;
                $data{vs} = '$(OBJDIR)/linkscript_' . $index . '.def';
                $data{ls} = '$(OBJDIR)/linkscript_' . $index . '.scr';
                push @ldFixups, \%data;

                push @scripts, $data{ls};
                push @scripts, '--version-script=' . $data{vs};

                push @preReqFiles, $data{vs};
                push @preReqFiles, $data{ls};

            }  else {
                # Normal operation. Linker is not broken
                push @preReqFiles, $def;
                if ( $1 =~ m~\.def$~ ) {
                    $def = '--version-script=' .$def; # Old def file
                }
                push @scripts, $def;
            }

        } elsif ( /^--MultiScan/i ) {
            $multi_scan = 1;

        } elsif ( /^--NoMultiScan/i ) {
            $multi_scan = 0;

        } elsif ( /^--SoNameSuffix=(.*)/i ) {
            $sosuffix = $1;

        } elsif (/^--NoVersionDll/i) {
            $noVersionedLib = 1;

        #   A 'node' file is a specific shared library for Electron applications
        #       They have a fixed file extension of .node on all platforms
        #       Do not generate a Versioned DLL
        } elsif (/^--Node$/i) {
            $isNodeFile = 1;
            $noVersionedLib = 1;
        } else {
            Message( "SHLD: unknown option $_ -- ignored\n" );
        }
    }

    #
    # Determine the 'soname' if none has been provided
    #
    $sosuffix = '.' . $ver
        unless ( defined $sosuffix );
    $sosuffix = ''
        if ( $noVersionedLib );

#.. Various library names
#
    if ( $isNodeFile ) {
        $linkname = "$name\$(GBE_TYPE).node";
        $linkname =~ s/^lib//;
    } else {
        $linkname = "$name\$(GBE_TYPE).$::so";
    }
    $shared = $noVersionedLib ? $linkname : "$linkname.$ver";
    $soname = "$linkname$sosuffix";

    my $shared_path = "\$(LIBDIR)/${shared}";
    my $dbg_path =  $shared_path . '.dbg';

#.. Cleanup rules
#
#   map     Map file
#   ln      Link from LIBDIR to BINDIR
#
    ToolsetGenerate( "\$(LIBDIR)/${name}.map" );
    ToolsetGenerate( "\$(LIBDIR)/${shared}" );
    ToolsetGenerate( "\$(BINDIR)/${soname}" );
    ToolsetGenerate( $dbg_path );

#.. Build rules
#
#   name        Base name
#   shared      Library name, includes GBE_TYPE specification
#
    my ($io) = ToolsetPrinter::New();
    my $dep = $io->SetShldTarget($shared);

    $io->Label( "Shared library", $name );
    PackageShlibAddFiles( $name, "\$(LIBDIR)/$shared" );
    PackageShlibAddFiles( $name, $dbg_path );

    $io->Prt( "\$(LIBDIR)/${shared}:\tSHBASE=${name}\n" );
    $io->Prt( "\$(LIBDIR)/${shared}:\tSHNAME=${soname}\n" );
    $io->Prt( "\$(LIBDIR)/${shared}: \\\n\t\t${dep}" );
    $io->Entry( "", "", " \\\n\t\t", ".$::o", @$pObjs );
    $io->Entry( "", "", " \\\n\t\t", "", @preReqFiles );
    $io->Prt( "\n\t\$(SHLD)" );
    $io->Prt( "\n\t\$(call LDSTRIP,$shared_path,$dbg_path)\n\n" );

#
#   Create soft links
#       'Real Name' to its 'Link Name'
#       'Real Name' to 'SoName' in the BINDIR (for testing I think)
#       'Real Name' to 'SoName' in the LIBDIR (if different)
#
    if ( $shared ne $linkname)
    {
        $io->Label( "Shared library Symbolic Links", $name );
        PackageShlibAddFiles( $name, "\$(LIBDIR)/$linkname", 'symlink=1' );
        $io->Prt( "\$(LIBDIR)/$linkname:\t\\\n" .
                  "\t\t\$(GBE_BINDIR)\\\n" .
                  "\t\t\$(LIBDIR)/${shared}\n" .
                  "\t\$(AA_PRE)(rm -f \$@; ln -s $shared \$@)\n\n" );
    }

    $io->Label( "Shared library BINDIR Symbolic Links", $name );
#    PackageShlibAddFiles( $name, "\$(BINDIR)/$soname", 'symlink=1' );
    $io->Prt( "\$(BINDIR)/$soname:\t\\\n" .
              "\t\t\$(GBE_BINDIR)\\\n" .
              "\t\t\$(LIBDIR)/${shared}\n" .
              "\t\$(AA_PRE)(rm -f \$@; ln -s ../\$(LIBDIR)/$shared \$@)\n\n" );

    if ( $soname ne $shared )
    {
        $io->Label( "Shared library SoName Symbolic Links", $name );
        PackageShlibAddFiles( $name, "\$(LIBDIR)/$soname", 'symlink=1' );
        $io->Prt( "\$(LIBDIR)/$soname:\t\\\n" .
                  "\t\t\$(GBE_LIBDIR)\\\n" .
                  "\t\t\$(LIBDIR)/${shared}\n" .
                  "\t\$(AA_PRE)(rm -f \$@; ln -s $shared \$@)\n" );
    }

#.. Linker command file
#
#       Now the fun part... piecing together a variable $(name_shld)
#       which ends up in the command file.
#
    $io->Newline();
    $io->SetTag( "${name}_shld" );              # command tag
    $io->SetTerm( "\n" );

    $io->Label( "Linker commands", $name );     # label

                                                # object list
    $io->ObjList( $name, $pObjs, \&ToolsetObjRecipe );

    ToolsetLibStd( $pLibs );                    # push standard libraries

    $io->Cmd( "-Wl," . join (',', @scripts) ) if (@scripts);

    $io->Cmd( "-Wl,--start-group" ) if ($multi_scan);
    $io->LibList( $name, $pLibs, \&ToolsetLibRecipe );
    $io->Cmd( "-Wl,--end-group" ) if ($multi_scan);

    $io->Newline();

    #.. Dependency link,
    #   Create a library dependency file
    #       Create command file to build applicaton dependency list
    #       from the list of dependent libraries
    #
    #       Create makefile directives to include the dependency
    #       list into the makefile.
    #
    $io->DepRules( $pLibs, \&ToolsetLibRecipe, "\$(LIBDIR)/${shared}" );
    $io->SHLDDEPEND($name, $soname);
}


###############################################################################
# Function        : ToolsetLD
#
# Description     : Takes the user options and builds the rules required to
#                   link the program 'name'.
#
# Inputs          : $name           - base name of the program
#                   $pArgs          - Ref to program arguments
#                   $pObjs          - Ref to program objects
#                   $pLibs          - Ref to program library list
#
# Returns         : Nothing
#
# Output:         : Rules and recipes to create a program
#                       Create program rules and recipes
#                       Create linker input script
#                       Create library dependency list
#                       Include library dependency information
#
sub ToolsetLD
{
    my( $name, $pArgs, $pObjs, $pLibs ) = @_;
    my $static = $::ScmCompilerOpts{'STATIC_PROGS'};
    my $multi_scan;

#.. Parse arguments
#
    foreach $_ ( @$pArgs )
    {
        if ( m/^--Static$/ ) {
            $static = 1;

        } elsif ( m/^--Shared/ ) {
            $static = 0;

        } elsif ( /^--MultiScan/i ) {
            $multi_scan = 1;

        } elsif ( /^--NoMultiScan/i ) {
            $multi_scan = 0;

        } else {
            Message( "LD: unknown option $_ -- ignored\n" );
        }
    }

#.. Names of programs and components
#
    my $base = "\$(BINDIR)/${name}";
    my $full = $base . $::exe;
    my $map  = $base . '.map';
    my $ld  =  $base . '.ld';
    my $dbg =  $base . '.dbg';

#.. Cleanup rules
#
    ToolsetGenerate( $ld );
    ToolsetGenerate( $map );
    ToolsetGenerate( $dbg );

#.. Build rules
#
    my ($io) = ToolsetPrinter::New();
    my $dep = $io->SetLdTarget( $name );

    $io->Prt( "$full : $dep " );
    $io->Entry( "", "", "\\\n\t", ".$::o ", @$pObjs );
    $io->Prt( "\n\t\$(LD)" );
    $io->Prt( "\n\t\$(call LDSTRIP,$full,$dbg)\n\n" );


#.. Linker command file
#
#       Now the fun part... piecing together a variable $(name_ld)
#       which ends up in the command file.
#
    $io->SetTag( "${name}_ld" );                # macro tag
    $io->SetTerm( "\n" );

    $io->Label( "Linker commands", $name );     # label

    $io->Cmd( "-static" ) if ($static);         # Link as a static program

                                                # object list
    $io->ObjList( $name, $pObjs, \&ToolsetObjRecipe );

    ToolsetLibStd( $pLibs );                    # push standard libraries

                                                # library list
    $io->Cmd( "-Wl,--start-group" ) if ($multi_scan);
    $io->LibList( $name, $pLibs, \&ToolsetLibRecipe );
    $io->Cmd( "-Wl,--end-group" ) if ($multi_scan);

    #   Add Compilers sysroot library path
    #   Only required for a 'native' compiler that is not fully installed on the host
    #   ie: The run-time can't locate the required glibc components
    if ($GCCRPath)
    {
        $io->Cmd( "-Wl,-rpath=$GCCRPath" );
        $io->Cmd( "-Wl,--dynamic-linker=$GCCRPath/ld-linux.so.2" ) if ($GCCRPath);
    }

    $io->Newline();

    #.. Dependency link,
    #   Create a library dependency file
    #       Create command file to build applicaton dependency list
    #       from the list of dependent libraries
    #
    #       Create makefile directives to include the dependency
    #       list into the makefile.
    #
    $io->DepRules( $pLibs, \&ToolsetLibRecipe, $base );
    $io->LDDEPEND();

#.. Package up the program and other artifacts
#
    PackageProgAddFiles ( $name, $full );
    PackageProgAddFiles ( $name, $dbg, "Class=debug" );

}

  #-------------------------------------------------------------------------------
# Function        : ToolsetPostprocess
#
# Description     : Last chance by the toolset to perform processing
#                   All Directives have been processed
#
#                   If automated unit test are being used,
#                   then we need to post process the results
#
# Inputs          : None
#
# Returns         :
#
sub ToolsetPostprocess
{
    ToolsetPreprocessTests();
    ToolsetPostprocessTests();
    ToolsetCollateTestResults();
    ToolsetGenLdScripts();
}

###############################################################################
# Function        : ToolsetPreprocessTests
#
# Description     : Generate $(OBJDIR)/lcov-baseline.info
#                   Generate full coverage info
#
# Inputs          : None
#
# Returns         : Nothing
#
# Output:         : Rules and recipes to run before unit tests
#
sub ToolsetPreprocessTests
{
    my ($io) = ToolsetPrinter::New();
    if ( $UseGcov && scalar(keys %::OBJSOURCE) > 0 )
    {
        #
        #   Setup Env so that the files created by GCOV are within the package
        #   being created. Elsewise they will be created all over the file
        #   system in the original location where the external objects were created.
        #
        $io->PrtLn('GCOV_PREFIX := $(abspath $(INTERFACEDIR)/gcov)' );
        $io->PrtLn('GCOV_PREFIX_STRIP := 0' );
        $io->PrtLn('export GCOV_PREFIX GCOV_PREFIX_STRIP' );
        $io->Newline();

        #
        #   GCOV_PKGBASE - used to 'extract' coverage artifacts
        #   Used to limit the coverage report to files that are within this package
        #   Ideally it should be the root of the package, but this is not always known
        #   Use the first two path elements of the current package
        #       Really just want to filter out compiler bits
        #

        $io->PrtLn('GCOV_PKGBASE :=  $(subst $(space),/,$(space)$(wordlist 1,2,$(subst /,$(space),$(CURDIR))))' );
        $io->PrtLn('GCOV_VERBOSE :=  $(if $(VERBOSE_OPT),,--quiet)' );
        $io->Newline();

        my $ruleName = 'preprocess_gcov_data';
        my $finaldir='$(LOCALDIR)/lcov/$(GBE_PLATFORM)$(GBE_TYPE)';
        my $final=$finaldir . '/lcov-final.info';

        ToolsetAddUnitTestPreProcess($ruleName);
        $io->PrtLn('.PHONY: ' . $ruleName );
        $io->PrtLn($ruleName . ': $(OBJDIR)' );
        $io->PrtLn("\t" . '${XX_PRE}$(rm) -f ${GCOV_PREFIX}/$(CURDIR)/$(OBJDIR)/*.gcda' );
        $io->PrtLn("\t" . '${XX_PRE}$(rm) -f $(OBJDIR)/*.gcda' );
        $io->PrtLn("\t" . '${XX_PRE}$(rm) -f ' . $final);
        $io->PrtLn("\t" . '${XX_PRE}lcov'
                        . ' $(GCOV_VERBOSE)'
                        . ' --capture'
                        . ' --initial'
                        . ' --base-directory $(CURDIR)'
                        . ' --directory $(OBJDIR)'
                        . ' --gcov-tool $(GCC_GCOVTOOL)'
                       . ' --output-file $(OBJDIR)/lcov-baseline.info' );
        $io->Newline();
        ToolsetGenerate( '$(OBJDIR)/lcov-baseline.info' );
    }
}


###############################################################################
# Function        : ToolsetPostprocessTests
#
# Description     : Merge this sub-components bits into the the final coverage
#                   file in lcov-baseline.info
#
#                   Need to locate the .gcda files and place them in the same directory
#                   as the .gcno files that were created during preprocssing
#
#                   Then we can add our trace bits into the file coverage file
#
# Inputs          : None
#
# Returns         : Nothing
#
# Output:         : Rules and recipes to run after the unit tests
#
sub ToolsetPostprocessTests
{
    my ($io) = ToolsetPrinter::New();
    if ( $UseGcov && scalar(keys %::OBJSOURCE) > 0 )
    {
        my $ruleName = 'postprocess_gcov_data';
        my $finaldir='$(LOCALDIR)/lcov/$(GBE_PLATFORM)$(GBE_TYPE)';
        my $final=$finaldir . '/lcov-final.info';

        ToolsetAddUnitTestPostProcess($ruleName);

        $io->PrtLn('.PHONY: ' . $ruleName );
        $io->PrtLn($ruleName . ':' );
        $io->PrtPart("\t" . '$(XX_PRE)if [ -d "${GCOV_PREFIX}/$(CURDIR)/$(OBJDIR)" ]; then' );
        $io->PrtPart("\t\t" . 'cd ${GCOV_PREFIX}/$(CURDIR);');
        $io->PrtPart("\t\t" . 'find $(OBJDIR) -name \'*.gcda\' -exec cp --parent {} $(CURDIR) \\; ;');
#        $io->PrtPart("\t" . 'else');
#        $io->PrtPart("\t\t" . 'echo NO .gcda DATA for $(CURDIR);');
        $io->PrtLn  ("\t" . 'fi' );
        $io->PrtLn  ("\t" . '$(XX_PRE)$(mkdir) -p ' . $finaldir);
        $io->PrtPart("\t" . '$(XX_PRE)GCDA_COUNT=$$(find $(OBJDIR) -name "*.gcda" | wc -l);' );
        $io->PrtPart("\t" . 'if [ "$${GCDA_COUNT}" -eq "0" ]; then ');
        $io->PrtPart("\t\t" . 'if [ ! -e ' . $final . " ]; then");
        $io->PrtPart("\t\t\t" . '$(cp) $(OBJDIR)/lcov-baseline.info ' . $final . ';');
        $io->PrtPart("\t\t" . 'else');
        $io->PrtPart("\t\t\t" . 'lcov'
                          . ' $(GCOV_VERBOSE)'
                          . $LcovExplicitBranch
                          . ' --add-tracefile $(OBJDIR)/lcov-baseline.info'
                          . ' --add-tracefile ' . $final
                          . ' --gcov-tool $(GCC_GCOVTOOL)'
                          . ' --output-file $(OBJDIR)/lcov-merge.info'
                          . ';');
        $io->PrtPart("\t\t\t" . '$(rm) -f ' . $final . ';');
        $io->PrtPart("\t\t\t" . '$(mv) $(OBJDIR)/lcov-merge.info ' . $final . ';');
        $io->PrtPart("\t\t" . 'fi' . ';');
        $io->PrtPart("\t" . 'else');
        $io->PrtPart("\t\t" . 'lcov'
                        . ' $(GCOV_VERBOSE)'
                        . $LcovExplicitBranch
                        . ' --capture'
                        . ' --base-directory $(CURDIR)'
                        . ' --directory $(OBJDIR)'
                        . ' --gcov-tool $(GCC_GCOVTOOL)'
                        . ' --output-file $(OBJDIR)/lcov-capture.info'
                        . ';');
        $io->PrtPart("\t\t" . 'if [ ! -e ' . $final . " ]; then");
        $io->PrtPart("\t\t\t" . 'lcov'
                          . ' $(GCOV_VERBOSE)'
                          . $LcovExplicitBranch
                          . ' --add-tracefile $(OBJDIR)/lcov-baseline.info'
                          . ' --add-tracefile $(OBJDIR)/lcov-capture.info'
                          . ' --gcov-tool $(GCC_GCOVTOOL)'
                          . ' --output-file ' .  $final
                          . ';');
        $io->PrtPart("\t\t" . 'else');
        $io->PrtPart("\t\t\t" . 'lcov'
                          . ' $(GCOV_VERBOSE)'
                          . $LcovExplicitBranch
                          . ' --add-tracefile $(OBJDIR)/lcov-baseline.info'
                          . ' --add-tracefile $(OBJDIR)/lcov-capture.info'
                          . ' --add-tracefile ' . $final
                          . ' --gcov-tool $(GCC_GCOVTOOL)'
                          . ' --output-file $(OBJDIR)/lcov-merge.info'
                          . ';');
        $io->PrtPart("\t\t\t" . '$(rm) -f ' . $final . ';');
        $io->PrtPart("\t\t\t" . '$(mv) $(OBJDIR)/lcov-merge.info ' . $final . ';');
        $io->PrtPart("\t\t" . 'fi' . ';');
        $io->PrtLn  ("\t" . 'fi');
        $io->Newline();

        ToolsetGenerate ('$(OBJDIR)/lcov-capture.info' );
        ToolsetGenerate ($final);

    }
}


###############################################################################
# Function        : ToolsetCollateTestResults
#
# Description     : Process the lcov-final.info by
#                       Extract parts generated by files in this package
#                       Remove parts generated by files in the interface directory
#                           They are the results of a 'BuildPkgArchive'
#
# Inputs          : None
#
# Returns         : Nothing
#
# Output:         : Rules and recipes to run after unit test result
#                   postprocessing.
#
sub ToolsetCollateTestResults
{
    my ($io) = ToolsetPrinter::New();
    if ( $UseGcov && scalar(keys %::OBJSOURCE) > 0 )
    {
        my $ruleName = 'collate_gcov_results';
        my $finaldir = '$(LOCALDIR)/lcov/$(GBE_PLATFORM)$(GBE_TYPE)';
        my $final     = $finaldir . '/lcov-final.info';
        my $finalTmp  = $finaldir . '/lcov-extracted.info';
        my $finalTmp1 = $finaldir . '/lcov-filtered.info';

        ToolsetGenerate ( $final );
        ToolsetGenerate ( $finalTmp );
        ToolsetGenerate ( $finalTmp1 );

        ToolsetAddUnitTestCollateProcess($ruleName);

        my $reportdir='$(PKGDIR)/lcov/$(GBE_PLATFORM)$(GBE_TYPE)';
        my $reportindex=$reportdir . '/index.html';
        $io->PrtLn('.PHONY: ' . $ruleName );
        $io->PrtLn($ruleName . ': ' . $reportindex);

        $io->Newline();
        $io->PrtLn($reportindex . ': ' . $final);
        $io->PrtPart("\t" . 'lcov'
                        . ' $(GCOV_VERBOSE)'
                        . $LcovExplicitBranch
                        . ' --extract '. $final .' "$(GCOV_PKGBASE)/*"'
                        . ' --gcov-tool $(GCC_GCOVTOOL)'
                        . ' --output-file ' . $finalTmp
                        . ';');
        $io->PrtPart("\t" . 'lcov'
                        . ' $(GCOV_VERBOSE)'
                        . $LcovExplicitBranch
                        . ' --remove '. $finalTmp .' "$(abspath $(INTERFACEDIR))/*"'
                        . ' --gcov-tool $(GCC_GCOVTOOL)'
                        . ' --output-file ' .$finalTmp1
                        . ';');
        $io->PrtLn("\t" . 'genhtml'
                      . ' $(GCOV_VERBOSE)'
                      . ' --frames'
                      . ' --show-details'
                      . ' --function-coverage'
                      . ' --branch-coverage'
                      . ' --output-directory ' . $reportdir
                      . ' --legend'
                      . ' --demangle-cpp'
                      . ' --title "$(GBE_PBASE) $(BUILDVER)"'
                      . ' ' . $finalTmp1);
        $io->Newline();
    }
}

#-------------------------------------------------------------------------------
# Function        : ToolsetGenLdScripts
#
# Description     : Generate Linker (ld) scripts to to support the Broken Linker
#                   Enabled via BROKEN_LDSCRIPTS
#                   The linker cannot handle a linkerscript with a 'VERSION" section
#                   The linker can handle a linker script with an EXTERN section
#                   and a --version-script
#
#                   This workaround will take the linker script and break it into two
#                   parts. This must be done at make-time in case the linker script is
#                   generated.
#
#                   Use a feature of Pattern Rules. Handling of multiple targets
#                   The pattern (%) is a '.'.
#                   It may be substituted as $* in the output.
#
#                   file1%out file2%out : infile
#                       SomeProg file1$*out file2$*out      - Using pattern substitution
#                       SomeProg file1.out file2.out        - Alternate command
#
# Inputs          : None
#
# Returns         : Nothing
#
sub ToolsetGenLdScripts
{
    return unless @ldFixups;
    Verbose('ToolsetGenLdScripts');

    my ($io) = ToolsetPrinter::New();
    $io->PrtLn('# Broken Linker Support');
    foreach my $entry (@ldFixups)
    {
        (my $ls = $entry->{ls}) =~ s~\.~%~;
        (my $vs = $entry->{vs}) =~ s~\.~%~;

        $io->Newline();
        $io->PrtLn("$vs $ls:  $entry->{src} \$(SCM_MAKEFILE)" );

        $io->PrtLn("\t\$(GBE_PERL) -Mjats_runtime_gcc -e splitscript -- --src=$entry->{src} --vs=$entry->{vs} --ls=$entry->{ls}" );
        ToolsetGenerate ($entry->{vs} );
        ToolsetGenerate ($entry->{ls} );
    }
}

###############################################################################
#   ToolsetARLINT( $name, \@args, \@objs )
#       This subroutine takes the user options and builds the rules
#       required to lint the static library 'name'.
#
#   Arguments:
#       --xxx                   No arguments currently defined
#
#   Output:
#       [ $(LIBDIR)/name$_lint:   .... ]
#           $(ARLINT)
#
###############################################################################

sub ToolsetARLINT
{
    if ( $UseCppcheck )
    {
        CppcheckAR( @_ );
    }
}


###############################################################################
#   ToolsetSHLDLINT $name, \@args, \@objs, \@libraries )
#       This subroutine takes the user options and builds the rules
#       required to lint the shared library 'name'.
#
#   Arguments:
#       (none)
#
#   Output:
#       [ $(LIBDIR)/$name_lint:   .... ]
#           $(SHLIBLINT)
#
###############################################################################

sub ToolsetSHLDLINT
{
    if ( $UseCppcheck )
    {
        CppcheckSHLD( @_ );
    }
}


###############################################################################
#   ToolsetLD( $name, \@args, \@objs, \@libraries, \@csrc, \@cxxsrc )
#       This subroutine takes the user options and builds the rules
#       required to lint the program 'name'.
#
#   Arguments:
#       (none)
#
#   Output:
#       [ $(BINDIR)/$name_lint:   .... ]
#           $(LDLINT)
#
###############################################################################

sub ToolsetLDLINT
{
    if ( $UseCppcheck )
    {
        CppcheckLD( @_ );
    }
}

########################################################################
#
#   Push standard "system" libraries. This is a helper function
#   used within this toolset.
#
#   Arguments:
#       $plib       Reference to library array.
#
########################################################################

sub ToolsetLibStd
{
}


########################################################################
#
#   Generate a linker object recipe.  This is a helper function used
#   within this toolset.
#
#   Arguments:
#       $io         I/O stream
#
#       $target     Name of the target
#
#       $obj        Library specification
#
########################################################################

sub ToolsetObjRecipe
{
    my ($io, $target, $obj) = @_;

    $io->Cmd( "\$(strip $obj).$::o" );
}


###############################################################################
#
#   Parse a linker lib list
#   This is a helper function used within this toolset
#
#   Arguments:
#       $target     Name of the target
#
#       $lib        Library specification
#
#       $tag        Tag (user specified)
#
#       $dp         If building a depend list, the full target name.
#
###############################################################################

sub ToolsetLibRecipe
{
    my ($io, $target, $lib, $dp) = @_;

    if ( ! defined($dp) ) {                     # linker
        $lib =~ s/^lib//;                       # .. remove leading 'lib'
        $io->Cmd( "-l$lib" );

    } else {                                    # depend
        $io->Cmd( "$dp:\t@(vlib2,$lib,GCC_LIB)" );

    }
}

#.. Successful termination
1;

