Subversion Repositories DevTools

Rev

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

#
# Module name   : vcwin32
# Module type   : Makefile system
# Compiler(s)   : ANSI C
# Environment(s): WIN32
#
# Description:
#       Visual C/C++ for WIN32
#
#............................................................................#

use strict;
use warnings;

#
#   Global data
#
my $pdb_file = "\$(GBE_PBASE)";
my $pdb_first_lib;
my $target_file_lib;
my $target_file_dll;
my $target_file_exe;
my $pdb_none;
my $toolset_name = 'vcwin32';                           # Toolset name : Error reporting

##############################################################################
#   Version information
#
my $toolset_info;
my $toolset_version = 'MSVC6';
my %ToolsetVersion =
    (
        'MSVC6'      =>  { 'def'        => 'vcwin32.def' ,
                           'buildcmd'   => 'msdev =DSW= /make "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'msdev =DSW= /make "=TYPE=" /clean /useenv' ,
                           'tmp'        => 'vc60',
                           'VSCOMPILER' => '1',
                           'def_targets' => [ 'ALL - RELEASE','ALL - DEBUG' ],
                           },

        'MS.NET2003' =>  { 'def'        => 'vcwin32_net2003.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc70',
                           'VSCOMPILER' => '2',
                           },

        'MS.NET2005' =>  { 'def'        => 'vcwin32_net2005.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc80',
                           'VSCOMPILER' => '3',
                           'GenManifest' => '1',
                           },

        'MS.NET2008' =>  { 'def'        => 'vcwin32_net2008.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc90',
                           'VSCOMPILER' => '3',
                           'GenManifest' => '1',
                           },

        'MS.NET2010' =>  { 'def'        => 'vcwin32_net2010.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc100',
                           'VSCOMPILER' => '3',
                           'GenManifest' => '1',
                           },

        'MS.NET2012' =>  { 'def'        => 'vcwin32_net2012.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc110',
                           'VSCOMPILER' => '3',
                           'GenManifest' => '1',
                           },

       'MS.NET2015' =>  { 'def'        => 'vs2015.def' ,
                          'buildcmd'   => 'devenv =DSW= /build "=TYPE=|x86" /useenv /out =LOG=' ,
                          'cleancmd'   => 'devenv =DSW= /clean "=TYPE=|x86" /useenv' ,
                          'tmp'        => 'vc110',
                          'VSCOMPILER' => '3',
                          'GenManifest' => '1',
                          },


       'VS2012_X64' =>   { 'def'        => 'vs2012_x64.def' ,
                           'buildcmd'   => 'devenv =DSW= /build "=TYPE=" /useenv /out =LOG=' ,
                           'cleancmd'   => 'devenv =DSW= /clean "=TYPE=" /useenv' ,
                           'tmp'        => 'vc110',
                           'VSCOMPILER' => '3',
                           'GenManifest' => '1',
                           'Machine'    => 'X64',
                           'Rules'      => 'vcwin64.rul',
                           'NoKludges'  => 1,
                         },

       'VS2015_X64' => { 'def'        => 'vs2015_x64.def' ,
                         'buildcmd'   => 'devenv =DSW= /build "=TYPE=|x64" /useenv /out =LOG=' ,
                         'cleancmd'   => 'devenv =DSW= /clean "=TYPE=|x64" /useenv' ,
                         'tmp'        => 'vc110',
                         'VSCOMPILER' => '3',
                         'GenManifest' => '1',
                         'Machine'    => 'X64',
                         'Rules'      => 'vcwin64.rul',
                         'NoKludges'  => 1,
                       },



    );


##############################################################################
#   ToolsetInit()
#       Runtime initialisation
#
##############################################################################

ToolsetInit();

sub ToolsetInit
{

#.. Parse arguments (Toolset arguments)
#
    Debug( "$toolset_name(@::ScmToolsetArgs)" );

    foreach $_ ( @::ScmToolsetArgs ) {
        if (/^--Version=(.*)/) {           # MS SDK Version
            $toolset_version = $1;

        } else {
            Message( "$toolset_name toolset: unknown option $_ -- ignored\n" );
        }
    }

#.. Parse arguments (platform arguments)
#
    Debug( "$toolset_name(@::ScmPlatformArgs)" );

    foreach $_ ( @::ScmPlatformArgs ) {
        if (/^--product=(.*)/) {                # GBE product

        } elsif (/^--Version=(.*)/) {           # MS SDK Version
            $toolset_version = $1;

        } elsif (/^--NoDinkumware/) {           # Dinkumware package test

        } else {
            Message( "$toolset_name toolset: unknown platform argument $_ -- ignored\n" );
        }
    }

#.. Validate SDK version
#   Currently six versions are supported
#       1) MSVC6            - As provided via Visual Studio
#       2) MSVS.net 2003    - Used to create .NET applications
#       3) MSVS.net 2005    - Used to create .NET applications
#       4) MSVS.net 2008    - Used to create .NET applications
#       5) MSVS.net 2010    - Used to create .NET applications
#       6) MSVS.net 2012    - Used to create .NET applications
#
    $toolset_info = $ToolsetVersion{$toolset_version};
    Error( "Unknown version: $toolset_version" ) unless ( defined $toolset_info );

    #
    #   Insert defaults
    #
    $toolset_info->{'Machine'} = 'X86' unless defined  $toolset_info->{'Machine'};
    $toolset_info->{'Rules'}   = 'vcwin32.rul' unless defined  $toolset_info->{'Rules'};


#.. Standard.rul requirements
#
    $::s = 'asm';
    $::o = 'obj';
    $::a = 'lib';
    $::so = 'dll';
    $::exe = '.exe';

#.. Toolset configuration
#
    $::ScmToolsetVersion = "1.0.0";             # our version
    $::ScmToolsetGenerate = 0;                  # generate optional
    $::ScmToolsetProgDependancies = 0;          # handle Prog dependancies myself
    $::ScmToolsetProperties{'LdFlagSpace'} = 1; # LdFlags support embedded spaces
                                                #

#.. define Visual C/C+ environment
    Init( "visualc" );
    ToolsetDefines( $toolset_info->{'def'} );
    PlatformDefine ("VSCOMPILER\t= $toolset_info->{'VSCOMPILER'}" );
    ToolsetRequire( "exctags" );                # and Exuberant Ctags
    ToolsetRules( $toolset_info->{'Rules'} );
    ToolsetRules( "standard.rul" );

#.. define PCLint envrionment
    ToolsetRequire( "pclint" );                 # using pclint
    PlatformDefine ("LINT_COFILE\t= co-msc60.lnt");
    PlatformDefine ("LINT_PRJ_FILE\t=lint.visualc");

#.. Cleanup rules
#
    ToolsetGenerate( "\$(OBJDIR)/$toolset_info->{'tmp'}.idb" );     # vc60.idb
    ToolsetGenerate( "\$(OBJDIR)/$toolset_info->{'tmp'}.pch" );
    ToolsetGenerate( "\$(OBJDIR)/$toolset_info->{'tmp'}.pdb" );
    ToolsetGenerate( "\$(PDB)" );
    ToolsetGenerate( "\$(PDB).tmp" );


#
#   The PDB files need to be compiled up with absolute paths to the source files
#   The easiest way to do this is with the makefile rules created with absolute
#   paths. Set a global flag to enable this option
#
    $::UseAbsObjects = 1;


#.. Extend the CompilerOption directive
#   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
        #
        'multithread_static' => { 'THREADMODE' , 'T' },      # -MT
        'multithread_dll'    => { 'THREADMODE' , 'D' },      # -MD Default
        'multithread_none'   => { 'THREADMODE' , 'L' },      # -ML

        'subsystem:windows'  => { 'LDSUBSYSTEM' , 'windows' },
        'subsystem:console'  => { 'LDSUBSYSTEM' , 'console' },

        'rtti'               => { 'USE_RTTI', 1 },
        'nopdb'              => { 'PDB_NONE', 1 },
        'pdb'                => { 'PDB_NONE', undef },
        'noaddlibs'          => { 'ADDLINKLIBS' , undef },      # Don't add link libs
        'addlibs'            => { 'ADDLINKLIBS' , '1' },        # default
        'noprecompilehdrs'   => { 'PRECOMPILEHDRS' , undef },   # Don't precompile headers
        'precompilehdrs'     => { 'PRECOMPILEHDRS' , '1' },     # default

        #
        #   Mimic some of the behavior of version-1 JATS
        #
        'jats_v1'            => { 'USE_JATS_V1' => '1' },
    );

    #
    #   Set default options
    #
    $::ScmCompilerOpts{'THREADMODE'} = 'D';
    $::ScmCompilerOpts{'ADDLINKLIBS'} = '1';
    $::ScmCompilerOpts{'PRECOMPILEHDRS'} = '1';

    #
    #   Some defaults
    #
    if ($toolset_info->{'VSCOMPILER'} >= 3) {
        push @::LDFLAGS, 'ignore:4099';     # Ignore PDB file missing
    }
}

##############################################################################
#   ToolsetPreprocess()
#       Process collected data before the makefile is generated
#       This, optional, routine is called from within MakefileGenerate()
#       It allows the toolset to massage any of the collected data before
#       the makefile is created
#
##############################################################################
sub ToolsetPreprocess
{
    #
    #   Extract the current state of PDB_NONE
    #   Are PDB files to be constructed.
    #
    $pdb_none = $::ScmCompilerOpts{'PDB_NONE'};
}

##############################################################################
#   ToolsetPostprocess
#       Process collected data as the makefile is generated
#       This, optional, routine is called from within MakefileGenerate()
#       It allows the toolset to massage any of the collected data before
#       the makefile is finally closed created
#
##############################################################################

sub ToolsetPostprocess
{
    MakeHeader('Toolset Postprocessed Information');

    #
    #   Attempt to fix bugs in Microsofts mspdbsrv in a multi-build environment
    #   Export EnvVar _MSPDBSRV_ENDPOINT_ with a unique text string
    #   Thus instances of mspdbsrv started by the build will be able to find there
    #   own instance.
    #
    #    _MSPDBSRV_ENDPOINT_ needs to be unique within the machine
    #    Its not intended to be globally unique - just not to generate two
    #    of them.
    #
    MakePrint("
#
#   Define endpoint to instantiate seperate instances of mspdbsrv
#
_MSPDBSRV_ENDPOINT_ = $::ScmBuildUuid
export _MSPDBSRV_ENDPOINT_
");

    #
    #   Specify the name of the global PDB file. This is used for all
    #   compiles other than those associated with building a DLL
    #
    #   The name of the PDB will be based on either
    #       The name of base package
    #       The name of the first static library created
    #

MakePrint("
#
#   Export the name of the common PDB file
#   All compiler information will be placed in this file
#   The name of the file MUST be the same as the name of the output library
#
PDB = \$(OBJDIR)/$pdb_file\$(GBE_TYPE).pdb
" );

    #
    #   Add values to the perl environment
    #   May be used by post processing tools to create Visual Studio Projects
    #
    $::TS_pdb_file = "\$(OBJDIR)/$pdb_file\$(GBE_TYPE).pdb" unless( $pdb_none );
    $::TS_sbr_support = 1;

    #
    #   Prioritorise target: EXE, DLL, LIB
    #
    for my $tgt ( $target_file_exe, $target_file_dll, $target_file_lib  )
    {
        if ( $tgt )
        {
            $::TS_target_file = $tgt;
            last;
        }
    }
}


###############################################################################
#   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( @_ );
}


###############################################################################
#   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
{
    ToolsetCC_common( "CC", @_ );
}

sub ToolsetCC_common
{
    my( $name, $source, $obj, $pArgs ) = @_;
    my( $cflags, $pdb );

    foreach $_ ( @$pArgs ) {
        if (/--Shared$/) {                      # Building a 'shared' object
            $cflags = "$cflags \$(SHCFLAGS)";
            $pdb = $::SHOBJ_LIB{$obj}
                if (exists $::SHOBJ_LIB{$obj} );

        } else {
            Message( "$toolset_name $name: unknown option $_ -- ignored\n" );
        }
    }

    MakePrint( "\n\t\$($name)\n" );
    MakePadded( 4, "\$(OBJDIR)/$obj.$::o:", "\tCFLAGS +=$cflags\n" )
        if ( $cflags );                         # object specific CFLAGS

    if ( $pdb && !$pdb_none )
    {
        #
        #   Determine the name of the PDB file
        #   If we are building a shared library then the name of the PDB
        #   MUST NOT be the same as the name of the library as there is
        #   some stange interaction with the linker ( 50% of the time )
        #   This is OK as the file will not be published
        #
        #   If building a static library then create a PDB of the same
        #   name as it may be published directly.
        #
        my $pdb_file;
        if ($cflags )
        {
            $pdb_file = "\$(OBJDIR)/${pdb}\$(GBE_TYPE)_shlib.pdb";
        }
        else
        {
            $pdb_file = "\$(OBJDIR)/${pdb}\$(GBE_TYPE).pdb";
        }

        MakePadded( 4, "\$(OBJDIR)/$obj.$::o:", "\tPDB = $pdb_file\n" );
        ToolsetGenerate( $pdb_file );
        ToolsetGenerate( $pdb_file . ".tmp" );
    }

    #
    #   Remove possible Source Browser Files
    #
    ToolsetGenerate( "\$(OBJDIR)/$obj.sbr" );
    MakePrint( "\n" );
}


###############################################################################
#   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
{
    ToolsetCC_common( "CXX", @_ );
}

###############################################################################
#   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:
#       --Def=name              Library definition module
#
#   Output:
#       [ $(LIBDIR)/name$.${a}:   .... ]
#           $(AR)
#
###############################################################################

sub ToolsetAR
{
    my( $name, $pArgs, $pObjs ) = @_;
    my( $def );
    my ( $res, @reslist );
    my $lib_base = "\$(LIBDIR)/${name}\$(GBE_TYPE)";
    my $lib_name = "$lib_base.${a}";


#.. Parse arguments
#
    $def = "";                                  # options
    $res = "";

    foreach $_ ( @$pArgs ) {
        if (/^--Def=(.*)/) {                    # library definition
            $def = "$1";

        } elsif (/^--Resource=(.*)/) {          # Resource definition
            ($res, @reslist) = ToolsetRClist( "$name", $1 );

        } else {                                # unknown
            Message( "$toolset_name AR: unknown option $_ -- ignored\n" );
        }
    }

#.. Resource Creation
#
    MakePrint( "#.. Library Resource ($name)\n\n" );
    ToolsetRCrecipe( $res, @reslist )
        if ( $res );

#.. Target
#
    MakePrint( "#.. Library ($name)\n\n" );     # label

    MakePrint( "$lib_name:\tLIBDEF=$def\n" ) if ($def);
    MakeEntry( "$lib_name:\t", "", "\\\n\t\t", ".$::o", @$pObjs );
    MakePrint( "\\\n\t\t$def" ) if ($def);
    MakePrint( "\\\n\t\t$res" ) if ($res);
    MakePrint( "\n\t\$(AR)" );

#
#   Track the name of the possible target file
#   Used when creating Visual Studio projects
#
    $target_file_lib = $lib_name;

#
#   To assist in debugging the static library it is nice to
#   keep the PDB file used to build the library, with the library.
#
#   If the library is being packaged or installed then add the PDB
#   file to the package / installation
#
#   NOTE: Due to the limitations of JATS/MicroSoft only one static
#   library can be built with a PDB file. The name of the PDB file
#   will be taken from the first static library encountered
#
    unless ( $pdb_first_lib )
    {
        $pdb_first_lib = $name;
        $pdb_file = $name;
    }
    else
    {
        Warning( "Multiple static libraries created with a common PDB file: $pdb_file, $name" )
            unless( $pdb_none );
    }

    PackageLibAddFiles( $name, "\$(OBJDIR)/$pdb_file\$(GBE_TYPE).pdb", "Class=debug" )
        unless( $pdb_none );
}


###############################################################################
#   ToolsetARLINT( $name, \@args, \@objs )
#       This subroutine takes the user options and builds the rules
#       required to build the library 'name'.
#
#   Arguments:
#       --xxx                   No arguments currently defined
#
#   Output:
#       [ $(LIBDIR)/name$_lint:   .... ]
#           $(ARLINT)
#
###############################################################################

sub ToolsetARLINT
{
    PCLintAR( @_ );
}


###############################################################################
#   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
{
    my ($name, $pArgs, $pLibs) = @_;
    MakePrint( "\n\t\$(ARMERGE)\n\n" );

    #
    #   Package up the PDB's with the library
    #   Note: The PDBs will be found in the OBJDIR
    #
    unless( $pdb_none )
    {
        for ( @{$pLibs} )
        {
            s~\$\(LIBDIR\)~\$(OBJDIR)~;
            PackageLibAddFiles( $name, "$_\$(GBE_TYPE).pdb", "Class=debug" );
        }
    }
}


###############################################################################
#   ToolsetSHLD $name, \@args, \@objs, \@libraries, $_ver )
#       This subroutine takes the user options and builds the rules
#       required to link a shared library
#
#   Arguments:
#       --Def=xxxx.def[,opts]           # Definition file
#           --MutualDll                 # SubOption: Generate a Mutual DLL
#       --MutualDll                     # Generate a Mutual DLL (requires --Def=xxx)
#       --StubOnly                      # Only generate the stub library (requires --Def=xxx)
#       --Resource=xxxx.rc              # Resource file
#       --ResourceOnly                  # Only resources in this DLL
#       --Implib                        # Alternate ruleset
#       --NoPDB                         # Do not package the PDB file
#       --NoImplib                      # Do not package the import library
#       --Entry=xxxxx                   # Entry point
#       --NoAddLibs                     # Do not add libraries
#       --Node                          # Node library for Electron
#                                         Fixed extension. No versioned dll, no .lib file
#
#   Output:
#
#       There is two supported rule sets differentiated by --Implib
#       The two are very similar and generated exportable files of:
#
#       --Implib
#           ${name}.lib         - Stub lib adresses the versioned DLL
#           ${name}.${ver}.dll
#
#       Default
#           ${name}.lib         - Stub lib addresses UN-versioned DLL
#           ${name}.dll
#           ${name}.${ver}.dll
#
#       Note: Each DLL has an associated PDB file
#
#       Generation is identical for both rulesets. The default form does
#       not export any stub library associated with the versioned DLL.
#
#   Implementation notes
#   The process of creating a DLL and associated import library (LIB) is
#
#           ${name}.${ver}.dep
#           ${name}.${ver}.pdb          - Exported
#           ${name}.${ver}.ilk
#           ${name}.${ver}.dll          - Exported
#           ${name}.${ver}.map
#           ${name}.lib                 - Exported + Optional
#           ${name}.exp
#
#       Where:    lib = name
#
#       #.. Rules ($lib)
#
#       $(LIBDIR)/${lib}.lib:               $(LIBDIR)/${lib}.${ver}.dll
#       $(LIBDIR)/${lib}.pdb:               $(LIBDIR)/${lib}.${ver}.dll
#       $(LIBDIR)/${lib}.${ver}.pdb:        $(LIBDIR)/${lib}.${ver}.dll
#
#       $(LIBDIR)/${lib}.${ver}.dep:        SHBASE=${name}
#       $(LIBDIR)/${lib}.${ver}.dep:        SHNAME=${lib}.${ver}
#       $(LIBDIR)/${lib}.${ver}.dep:        \$(LIBDIR)
#       $(LIBDIR)/${lib}.${ver}.dep:        Makefile
#           $(SHLDDEPEND)
#
#       $(LIBDIR)/${lib}.${ver}.${so}:      SHBASE=${name}
#       $(LIBDIR)/${lib}.${ver}.${so}:      SHNAME=${lib}.${ver}
#       $(LIBDIR)/${lib}.${ver}.${so}:      CFLAGS+=$(SHCFLAGS)
#       $(LIBDIR)/${lib}.${ver}.${so}:      CXXLAGS+=$(SHCXXFLAGS)
#       $(LIBDIR)/${lib}.${ver}.${so}:      ${def}
#       $(LIBDIR)/${lib}.${ver}.${so}:      $(OBJDIR)/${name} \
#                       object list ... \
#                       $(LIBDIR)/${lib}.${ver}.dep
#           $(SHLD)
#           @$(cp) -f $(LIBDIR)/${lib}.${ver}.pdb $(LIBDIR)/${lib}.pdb
#
#       ifneq "$(findstring $(IFLAG),23)" ""
#       -include        "$(LIBDIR)/${lib}.${ver}.dep"
#       endif
#
#       #.. Linker commands ($lib)
#
#       ${lib}_ld       += ...
#               standard flags                          \
#               -implib:$${lib}.lib
#
#       #.. Linker commands ($lib)
#
#       ${lib}_shdp     += ...
#
###############################################################################

sub ToolsetSHLD
{
    our( $name, $pArgs, $pObjs, $pLibs, $_ver ) = @_;
    our( $def, $mutual_dll, $res, @reslist, $doimplib, $stub_only );
    our( $no_implib, $no_pdb, $resource_only );
    our( $entry, $noaddlibs );
    our( $isNodeFile );

#.. Parse arguments
#
    $def = "";                                  # options
    $doimplib = 0;
    $res = "";
    $no_pdb = $pdb_none;
    $isNodeFile = 0;

    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
            #
            $def = MakeSrcResolve($1);

            #
            #   Process sub options to --Def
            #
            next unless ($2);
            if ( $3 =~ /^--MutualDll$/ ) {
                $mutual_dll = 1;
            } else {
                Message( "$toolset_name SHLD: unknown option $_ -- ignored\n" );
            }

        } elsif (/^--Resource=(.*)/) {          # Resource definition
            ($res, @reslist) = ToolsetRClist( "$name/$name", $1 );

        } elsif (/^--ResourceOnly/) {          # Resource definition
            $resource_only = 1;

        } elsif (/^--Implib$/) {
            $doimplib = 1;

        } elsif (/^--NoImplib$/) {
            $no_implib = 1;

        } elsif (/^--NoPDB$/) {
            $no_pdb = 1;

        } elsif (/^--Entry=(.*)/) {
            $entry = $1;

        } elsif (/^--NoAddLib/) {
            $noaddlibs = 1;

        } elsif (/^--MutualDll$/) {
            $mutual_dll = 1;

        } elsif (/^--Stubonly/) {
            $stub_only = 1;

        } elsif (/^--Node$/i) {
            $isNodeFile = 1;

        } else {                                # unknown
            Message( "$toolset_name SHLD: unknown option $_ -- ignored\n" );
        }
    }

    #
    #   Sanity test
    #
    Error ("$toolset_name SHLD:Stubonly option requires --Def=file ")
        if ( $stub_only && ! $def );

    Error ("$toolset_name SHLD:MutualDll option requires --Def=file ")
        if ( $mutual_dll && ! $def );



#.. Build rules
#
#   base    -   Basic name of the DLL
#   name    -   Name of the Export library (Optional)
#   lib     -   Name of the DLL
#
    sub BuildSHLD
    {
        my ($base, $name, $lib) = @_;
        my $ext = $isNodeFile ? '.node' : ".$::so"; 
        my $full = $lib.$ext;
        my $link_with_def;

    #.. Cleanup rules
    #
    #   ld      Linker command file
    #   map     Map file
    #   pdb     Microsoft C/C++ program database
    #   ilk     Microsoft Linker Database
    #
        ToolsetGenerate( "\$(LIBDIR)/${lib}.ld" );
        ToolsetGenerate( "\$(LIBDIR)/${lib}.map" );
        ToolsetGenerate( "\$(LIBDIR)/${lib}.exp" );
        ToolsetGenerate( "\$(LIBDIR)/${lib}.ilk" );
        ToolsetGenerate( "\$(LIBDIR)/${full}" );
        ToolsetGenerate( "\$(LIBDIR)/${full}.manifest" ) if $toolset_info->{'GenManifest'};

    #.. Linker rules
    #
        my ($io) = ToolsetPrinter::New();

        my $import_lib;
        my $export_file;

        if ( $name && $def && ($mutual_dll || $stub_only ) )
        {
            #
            #   Creating an Export library from user .DEF file
            #   It is possible to create the stub library in the LIB phase
            #   which allows DLLs with mutual imports
            #
            $io->Label( "Import(stub) library for mutual exports", $name );
            $export_file = "\$(LIBDIR)/${name}.exp";

            #
            #   Rules and recipe to generate the stub library
            #
            $io->Prt( "\$(LIBDIR)/${name}.exp:\t\$(LIBDIR)/${name}.${a}\n" );
            $io->Prt( "\$(LIBDIR)/${name}.${a}:\tLIBDEF=$def\n" );
            $io->Prt( "\$(LIBDIR)/${name}.${a}:\tLIBNAME=$lib\n" );
            $io->Entry( "\$(LIBDIR)/${name}.${a}: \\\n\t\t\$(OBJDIR)/${base}",
                                            "", " \\\n\t\t", ".$::o", @$pObjs );
            $io->Prt( " \\\n\t\t$def" );
            $io->Prt( "\n\t\t\$(AR)\n" );
            $io->Newline();

            #
            #   Files to be cleanup up
            #
            ToolsetGenerate( "\$(LIBDIR)/${name}.exp" );
            ToolsetGenerate( "\$(LIBDIR)/${name}.${a}" );

            #
            #   If the DLL is being packaged/installed then add the static
            #   stub library to the packaging lists as a static library
            #   This will allow the stub library to be installed with the
            #   static libraries and thus allow DLL's with mutual imports
            #
            PackageShlibAddLibFiles ($base, "\$(LIBDIR)/${name}.${a}" )
                unless ($resource_only);

            #
            #   Add the stub library to the list of libraries being created
            #   Note: Don't do if not created with .DEF file
            #
            push @::LIBS, $base;

        }
        else
        {
            #
            #   The stub library is created as part of the DLL generation
            #   Whether we like it or not - so we need to control the name
            #   and location
            #
            my $slname = ($name) ? $name : $lib;
            $import_lib = "\$(LIBDIR)/${slname}.${a}";

            $io->Label( "Import(stub) library", $slname );
            $io->Prt( "$import_lib:\t\$(LIBDIR)/${full}\n" );
            $io->Newline();

            ToolsetGenerate( $import_lib );
            ToolsetGenerate( "\$(LIBDIR)/${slname}.exp" );

            #
            #   Package the generated stub library, if it is being
            #   created on request.
            #
            #   Package it with the shared libaries and not the static
            #   libraries as it will be created with the shared library
            #
            PackageShlibAddFiles ($base, $import_lib, 'Class=lib' )
                if ($name && ! $no_implib && ! $resource_only);

            #
            #   Indicate that we will be linking with a DEF file
            #
            $link_with_def = 1 if ( $def );
        }

        #
        #   If we are only creating a stub library, then the hard work has been
        #   done.
        #
        return
            if ($stub_only);

        $io->Label( "Shared library", $name );

        #
        #   The process of creating a DLL will generate PDB file
        #   Control the name of the PDB file
        #
        my $pdb_file = "\$(LIBDIR)/${lib}.pdb";
        unless( $no_pdb )
        {
            $io->Prt( "$pdb_file:\t\$(LIBDIR)/${full}\n" );
            ToolsetGenerate( $pdb_file );
        }

        #
        #   Package the PDB file up with the DLL
        #   Package the DLL - now that we know its proper name
        #
        PackageShlibAddFiles( $base, $pdb_file, 'Class=debug', "NoTarget=1" ) unless $no_pdb ;
        PackageShlibAddFiles( $base, "\$(LIBDIR)/${full}.manifest", 'Exists=1', "NoTarget=1" ) if ( $toolset_info->{'GenManifest'} );
        PackageShlibAddFiles( $base, "\$(LIBDIR)/${full}" );

        #
        #   Generate Shared Library dependency information
        #
        my $dep = $io->SetShldTarget( $lib );

        #
        #   Generate rules and recipes to create the body of the shared
        #   library. Several build variables are overiden when creating
        #   a shared library.
        #
        $io->Prt( "\$(LIBDIR)/${full}:\tSHBASE=${lib}\n" );
        $io->Prt( "\$(LIBDIR)/${full}:\tSHNAME=${lib}\n" );
        $io->Prt( "\$(LIBDIR)/${full}:\tCFLAGS+=\$(SHCFLAGS)\n" );
        $io->Prt( "\$(LIBDIR)/${full}:\tCXXLAGS+=\$(SHCXXFLAGS)\n" );
        $io->Prt( "\$(LIBDIR)/${full}:\t$export_file\n" ) if ($export_file );
        $io->Prt( "\$(LIBDIR)/${full}:\t$res\n" ) if ( $res );

        $io->Entry( "\$(LIBDIR)/${full}: $dep \\\n\t\t\$(OBJDIR)/${base}",
            "", " \\\n\t\t", ".$::o", @$pObjs );

        $io->Prt( " \\\n\t\t$def" ) if ( $link_with_def );
        $io->Prt( "\n\t\$(SHLD)\n" );

        $io->Newline();

        #.. Linker command file
        #
        #       Now the fun part... piecing together a variable ${name}_shld
        #       which ends up in the command file.
        #
        $io->SetTag( "${lib}_shld" );          # command tag

        $io->Label( "Linker commands", $name ); # label

        $io->Cmd( "-dll" );
        $io->Cmd( "-noentry" )if ($resource_only);
        $io->Cmd( "-machine:" . $toolset_info->{'Machine'} );
        $io->Cmd( "-base:0x10000000" );
        $io->Cmd( "-def:$def" ) if ($link_with_def);
        $io->Cmd( "-out:\$(subst /,\\\\,\$(LIBDIR)/${full})" );
        $io->Cmd( "-implib:\$(subst /,\\\\,$import_lib)" )  if ($import_lib);
        $io->Cmd( "-pdb:\$(subst /,\\\\,$pdb_file)" )       unless ( $no_pdb );
        $io->Cmd( "-debug:none" )                           if ($no_pdb);
        $io->Cmd( "-pdb:none" )                             if ($no_pdb);
        $io->Cmd( "-entry:$entry" )                         if ($entry);
        $io->Cmd( "-map:\$(subst /,\\\\,\$(LIBDIR)/${lib}).map" );
        $io->Cmd( "-nodefaultlib:LIBC" );
        $io->Cmd( "\$(subst /,\\\\,$res)" )                 if ( $res );
        $io->Cmd( "\$(subst /,\\\\,$export_file)" )         if ( $export_file );

                                                # object list
        $io->ObjList( $name, $pObjs, \&ToolsetObjRecipe );

                                                # library list
        $io->LibList( $name, $pLibs, \&ToolsetLibRecipe );

        $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)/${full}" );
        $io->SHLDDEPEND( $name, $lib );
    }   # End BuildSHLD

    ToolsetLibStd( $pLibs )                    # push standard libraries
        unless ( $noaddlibs );

    if ( $doimplib ) {
        #
        #   --Implib flavor will create
        #       a) Import library   $name$(GBE_TYPE).lib
        #       b) Versioned DLL    $name$(GBE_TYPE).xx.xx.xx.dll
        #
        $target_file_dll = "\$(LIBDIR)/$name\$(GBE_TYPE).$_ver.$::so";
        BuildSHLD(
            "$name",                        # Base Name
            "$name\$(GBE_TYPE)",            # Name of Export Lib
            "$name\$(GBE_TYPE).$_ver");     # Name of the DLL + PDB

    } elsif ($isNodeFile) {
        #
        #   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
        #       Do not generate an export library
        #   --Node flavor will create
        #       a) Unversioned DLL $name$(GBE_TYPE).node
        #   
        $target_file_dll = "\$(LIBDIR)/$name\$(GBE_TYPE).node";
        BuildSHLD( "$name", ""                  , "$name\$(GBE_TYPE)" )

    } else {
        #
        #   Original flavor will create
        #       a) Import library   $name$(GBE_TYPE).lib    ---+
        #       b) Unversioned DLL  $name$(GBE_TYPE).dll    <--+
        #       c) Versioned DLL    $name$(GBE_TYPE).xx.xx.xx.dll
        #
        MakePrint(
            "# .. Versioned image\n\n".
            "\$(LIBDIR)/${name}\$(GBE_TYPE).$_ver.$::so:\t".
            "\$(LIBDIR)/${name}\$(GBE_TYPE).$::so\n".
            "\n" );

        $target_file_dll = "\$(LIBDIR)/$name\$(GBE_TYPE).$::so";
        BuildSHLD( "$name", ""                  , "$name\$(GBE_TYPE).$_ver" );
        BuildSHLD( "$name", "$name\$(GBE_TYPE)" , "$name\$(GBE_TYPE)" );
    }

    #.. Resource File
    #
    ToolsetRCrecipe( $res, @reslist )
        if ( $res );
}


###############################################################################
#   ToolsetSHLDLINT $name, \@args, \@objs, \@libraries )
#       This subroutine takes the user options and builds the rules
#       required to lint the program 'name'.
#
#   Arguments:
#       (none)
#
#   Output:
#       [ $(LIBDIR)/$name_lint:   .... ]
#           $(SHLIBLINT)
#
###############################################################################

sub ToolsetSHLDLINT
{
    PCLintSHLIB( @_ );
}

###############################################################################
# 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 ( $res, @reslist );
    my $no_pdb =$pdb_none;
    our( $entry, $noaddlibs );

#.. Parse arguments
#
    foreach ( @$pArgs ) {
        if (/^--Resource=(.*)/) {               # Resource definition
            ($res, @reslist) = ToolsetRClist( $name, $1 );

        } elsif (/^--NoPDB$/) {
            $no_pdb = 1;

        } elsif (/^--Entry=(.*)/) {
            $entry = $1;

        } elsif (/^--NoAddLib/) {
            $noaddlibs = 1;

        } else {
            Message( "$toolset_name LD: unknown option $_ -- ignored\n" );

        }
    }

#.. Names of important files
#
    my $base     = "\$(BINDIR)/${name}";
    my $full     = $base . $::exe;
    my $map      = $base . '.map';
    my $pdb      = $base . '.pdb';
    my $manifest = $full . '.manifest';


#.. Cleanup rules
#
#   ld              Linker command file
#   map             Map file
#   pdb             Microsoft C/C++ program database
#   ilk             Microsoft Linker Database
#   res             Compiled resource script
#   exe.manifest    Manifest file (VS2005 and above)
#
    ToolsetGenerate( $map );
    ToolsetGenerate( $pdb );
    ToolsetGenerate( $base . '.ld' );
    ToolsetGenerate( $base . '.ilk' );
    ToolsetGenerate( $manifest ) if $toolset_info->{'GenManifest'};

    #
    #   Under some conditions the creation of a program will also create
    #   an exp and lib files. May be related to an attempt to create a prog
    #   and a dll from the same code
    #
    ToolsetGenerate( $base . '.exp' );
    ToolsetGenerate( $base . '.lib' );


#.. Toolset Printer
#
    my ($io) = ToolsetPrinter::New();
    my $dep = $io->SetLdTarget( $name );

#.. Linker command
#
    $io->Prt( "$full : $dep " );
    $io->Entry( "", "", "\\\n\t", ".$::o ", @$pObjs );
    $io->Prt( "\\\n\t$res " ) if ( $res );
    $io->Prt( "\n\t\$(LD)\n\n" );


#.. Linker command file
#
#       Now piece together a variable $(name_ld) which ends up in
#       the command file linking the application.
#
    $io->SetTag( "${name}_ld" );                # macro tag

    $io->Label( "Linker commands", $name );     # label

    $io->Cmd( "-out:\$(subst /,\\\\,$full)" );
    $io->Cmd( "-pdb:\$(subst /,\\\\,$pdb)" )        unless ($no_pdb);
    $io->Cmd( "-debug:none" )                       if ($no_pdb);
    $io->Cmd( "-pdb:none" )                         if ($no_pdb);
    $io->Cmd( "-entry:$entry" )                     if ($entry);
    $io->Cmd( "-map:\$(subst /,\\\\,$map)" );
    $io->Cmd( "-nodefaultlib:LIBC" );
    $io->Cmd( "-machine:" . $toolset_info->{'Machine'} );
    $io->Cmd( "\$(subst /,\\\\,$res)" )             if ( $res );

    ToolsetLibStd( $pLibs ) unless ( $noaddlibs );      # push standard libraries
    $io->ObjList( $name, $pObjs, \&ToolsetObjRecipe );  # object list
    $io->LibList( $name, $pLibs, \&ToolsetLibRecipe );  # library list
    $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, $full );
    $io->LDDEPEND();

#.. Compile up the resource file
#
    ToolsetRCrecipe( $res, @reslist )
        if ( $res );

#.. Package up the PDB file with the program
#
    PackageProgAddFiles ( $name, $full );
    PackageProgAddFiles ( $name, $manifest, 'Exists=1' ) if ( $toolset_info->{'GenManifest'} );
    PackageProgAddFiles ( $name, $pdb, "Class=debug" ) unless ( $no_pdb );

#
#   Track the name of the possible target file
#   Used when creating Visual Studio projects
#
    $target_file_exe = $full;
}


###############################################################################
#   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
{
    PCLintLD( @_ );
}


########################################################################
#
#   Push standard "system" libraries. This is a helper function
#   used within this toolset.
#
#   Arguments:
#       $plib       Reference to library array.
#
########################################################################

sub ToolsetLibStd
{
    my ($plib) = @_;

    #
    #   Only add libraries if required
    #
    return unless( $::ScmCompilerOpts{'ADDLINKLIBS'} );


    #
    #   Appears to be a kludge that no one is happy to remove
    #   If we are inluding cs or csx libraries then add some more
    #
    unless ($toolset_info->{'NoKludges'}) {
        foreach (@$plib) {
            if ( $_ eq 'csx$(GBE_TYPE)' || $_ eq 'cs$(GBE_TYPE)' )
            {                                       # .. core services
                UniquePush( $plib, "KERNEL32" );
                UniquePush( $plib, "WINUX32\$(GBE_TYPE)" );
                UniquePush( $plib, "WS2_32" );
                last;
            }
        }
    }

    #
    #   Multithreaded DLL
    #
    push @$plib, "--ifeq=THREADMODE:D";
    push @$plib, "--ifdebug";
    push @$plib, "MSVCRTD";

    push @$plib, "--ifeq=THREADMODE:D";
    push @$plib, "--ifprod";
    push @$plib, "MSVCRT";

    #
    #   Static Multithread library
    #
    push @$plib, "--ifeq=THREADMODE:T";
    push @$plib, "--ifdebug";
    push @$plib, "LIBCMTD";

    push @$plib, "--ifeq=THREADMODE:T";
    push @$plib, "--ifprod";
    push @$plib, "LIBCMT";

    #
    #   User library
    #
    push @$plib, "USER32";
}


########################################################################
#
#   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( "\$(subst /,\\\\,\$(strip $obj)).$::o" );
}


########################################################################
#
#   Generate a linker/depend library recipe.  This is a helper function
#   used within this toolset.
#
#   Arguments:
#       $io         I/O stream
#
#       $target     Name of the target
#
#       $lib        Library specification
#
#       $dp         If building a depend list, the full target name.
#
########################################################################

sub ToolsetLibRecipe
{
    my ($io, $target, $lib, $dp) = @_;

    return                                      # ignore (compat)
        if ( $lib eq "rt" ||        $lib eq "thread" ||
             $lib eq "pthread" ||   $lib eq "nsl" ||
             $lib eq "socket" );

    if ( !defined($dp) ) {                      # linker
        $io->Cmd( "\$(subst /,\\\\,\$(strip $lib)).$::a" );

    } else {                                    # depend
        $io->Cmd( "$dp:\t@(vlib2,$lib,LIB)" );
    }
}


########################################################################
#
#   Parse resource file data
#   This is a helper function used within this toolset
#
#   Arguments   : $1  BaseName
#                 $2  The users resource list
#                     This is a list of comma seperated files
#                     The first file is the main resource script
#
#   Returns     : An array of resource files with full pathnames
#                 [0] = The output file
#                 [1] = The input file
#                 [..] = Other input files
#
########################################################################

sub ToolsetRClist
{
    my ($name, $files) = @_;
    my @result;

    #
    #   Generate the name of the output file
    #
    push @result, "\$(OBJDIR)/$name.res";

    #
    #   Process each user file
    #
    for (split( '\s*,\s*', $files ))
    {
        #
        #   Locate the file.
        #   If it is a generate file so it will be in the SRCS hash
        #   Other wise the use will have to use Src to locate the file
        #
        push @result, MakeSrcResolve($_);
    }

    #
    #   Return the array to the user
    #
    return @result;
}


########################################################################
#
#   Generate a resource file recipe
#   This is a helper function used within this tool
#
#   Arguments   : $1  Output resource file
#                 ..  Input resource files
#
########################################################################

sub ToolsetRCrecipe
{
    my ($out, @in) = @_;

    #
    #   Cleanup
    #
    ToolsetGenerate( $out );

    #
    #   Recipe
    #
    MakePrint( "\n#.. Compile Resource file: $out\n\n" );
    MakePrint( "$out:\t\$(GBE_PLATFORM).mk\n" );
    MakeEntry( "$out:\t", "", "\\\n\t\t", " ", @in );
    MakePrint( "\n\t\$(RC)\n" );
    MakePrint( "\n" );
}

########################################################################
#
#   Generate a project from the provided project solution file
#   This is aimed at .NET work
#
#   Arguments   : $name             - Base name of the project
#                 $solution         - Path to the solutionn file
#                 $pArgs            - Project specific options
#
########################################################################

my $project_defines_done = 0;
sub ToolsetPROJECT
{
    my( $name, $solution ,$pArgs ) = @_;
    my $buildcmd = $toolset_info->{'buildcmd'};
    my $cleancmd = $toolset_info->{'cleancmd'};
    my $release = ${$toolset_info->{'def_targets'}}[0] || 'RELEASE';
    my $debug =   ${$toolset_info->{'def_targets'}}[1] || 'DEBUG';

    #
    #   Process options
    #
    foreach ( @$pArgs ) {
        if ( m/^--TargetProd*=(.+)/ ) {
            $release = $1;

        } elsif ( m/^--TargetDebug=(.+)/ ) {
            $debug = $1;

        } else {
            Message( "$toolset_name PROJECT: unknown option $_ -- ignored\n" );
        }
    }

    my ($io) = ToolsetPrinter::New();

    #
    #   Setup toolset specific difinitions. Once
    #
    unless( $project_defines_done )
    {
        $project_defines_done = 1;
        $io->PrtLn( 'project_target = $(if $(findstring 1,$(DEBUG)),$2,$1)' );
        $io->Newline();
    }

    #
    #   Process the build and clean commands
    #       Substitute arguments
    #           =TYPE=
    #           =LOG=
    #           =DSW=
    #
    $buildcmd =~ s~=TYPE=~\$(call project_target,$release,$debug)~g;
    $buildcmd =~ s~=LOG=~$name\$(GBE_TYPE).log~g;
    $buildcmd =~ s~=DSW=~$solution~g;

    $cleancmd =~ s~=TYPE=~\$(call project_target,$release,$debug)~g;
    $cleancmd =~ s~=LOG=~$name\$(GBE_TYPE).log~g;
    $cleancmd =~ s~=DSW=~$solution~g;

    #
    #   Generate the recipe to create the project
    #   Use the set_<PLATFORM>.sh file to extend the DLL search path
    #
    $io->Label( "Build project", $name );
    $io->PrtLn( "Project_$name: $solution \$(INTERFACEDIR)/set_$::ScmPlatform.sh" );

    $io->PrtLn( "\t\$(XX_PRE)( \$(rm) -f $name\$(GBE_TYPE).log; \\" );
    $io->PrtLn( "\t. \$(INTERFACEDIR)/set_$::ScmPlatform.sh; \\" );
    $io->PrtLn( "\t\$(show_environment); \\" );
    $io->PrtLn( "\t$buildcmd; \\" );
    $io->PrtLn( "\tret=\$\$?; \\" );
    $io->PrtLn( "\t\$(GBE_BIN)/cat $name\$(GBE_TYPE).log; \\" );
    $io->PrtLn( "\texit \$\$ret )" );
    $io->Newline();

    #
    #   Generate the recipe to clean the project
    #
    $io->Label( "Clean project", $name );
    $io->PrtLn( "ProjectClean_$name: $solution" );
    $io->PrtLn( "\t-\$(XX_PRE)$cleancmd" );
    $io->PrtLn( "\t-\$(XX_PRE)\$(rm) -f $name\$(GBE_TYPE).log" );
    $io->Newline();

}


#.. Successful termination
1;