Subversion Repositories DevTools

Rev

Rev 289 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#
# Module name   : DELPHI7
# Module type   : Makefile system
# Compiler(s)   : Delphi Pascal
# Environment(s): WIN32
#
# Description:
#       Delphi7 for WIN32
#............................................................................#

use strict;
use warnings;
use FileUtils;

################################################################################
#   Globals
#
my $toolset_version;
my $implib_found;

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

ToolsetInit();

sub ToolsetInit
{

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

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

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

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

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

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

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

#.. Validate SDK version
#   Currently only one is supported
#       1) Delphi7 installed
#
    Error( "Unknown version: $toolset_version" ) if ( defined $toolset_version );


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

#.. Toolset configuration
#
    $::ScmToolsetVersion = "1.0.0";             # our version
    $::ScmToolsetGenerate = 0;                  # generate optional
    $::ScmToolsetProgDependancies = 0;          # handle Prog dependancies myself
    $::ScmToolsetSingleType = 1;                # Cannot support debug and production builds

#.. define Delphi7 environment
    Init( "delphi7" );
    ToolsetDefines( "delphi7.def" );
    ToolsetRequire( "exctags" );                # and Exuberant Ctags
    ToolsetRules( "delphi7.rul" );
    ToolsetRules( "standard.rul" );

    #.. 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
        #
        'subsystem:windows'  => { 'LDSUBSYSTEM' , '-CG' },
        'subsystem:console'  => { 'LDSUBSYSTEM' , '-CC' },
    );

    #
    #   Set default options
    #
    $::ScmCompilerOpts{'LDSUBSYSTEM'} = '-CG';

#.. Cleanup rules
#   None at the moment as we only build projects
#
    return 1;
}

#-------------------------------------------------------------------------------
# Function        : ToolsetGenLibName
#
# Description     : Function to generate the names of static libraries
#                   Will be used if it exists
#
# Inputs          : $name                   - Base Name of the library
#
# Returns         : Path to generated name
#
sub ToolsetGenLibName
{
    my ($name) = @_;
    return "$name.$::a";
}

#-------------------------------------------------------------------------------
# Function        : ToolsetPreprocess
#
# Description     : Called once all the user directives have been parsed
#                   In this toolset it is used as a hook to allow
#                   the creation of a 'simple' target to perform some
#                   level of dependency testing
#
#                   Basically. Create a target that consists on ALL the
#                   source files. This is then used as a prerequisite
#                   for all Libs, SharedLibs and Progs
#
#                   It is crude and ugly - but it does the job
#                   Would be better to parse the Delphi project files
#                   and determine the prerequisites ourselves, but ...
#
# Inputs          : None
#
# Returns         : Nothing directly
#
sub ToolsetPreprocess
{
    my @dlist;

    foreach ( values %::SRCS )
    {
        #
        #   Strip out known project files
        #   Must leave in .pas files
        #
        next if ( m~\.dpr$~i );
        next if ( m~\.dpk$~i );
        next if ( m~^descpkg$~ );
        push @dlist, $_;
    }

    if ( @dlist )
    {
        #
        #   Would like to use nice function that create nice text, but
        #   at the moment the makefile is not yet being written
        #
        #   Trick: Create an in-memory filehandle and use it
        #          The MAKEFILE handle is used by nice writers
        #
        my $data;
        local (*MAKEFILE);
        open(MAKEFILE, '>>', \$data) || Error ("Cannot open in-memory file");

        MakeHeader  ( "All Delphi Sources");
        MakeDefEntry( "DELPHISRCS", "=",  \@dlist );

        close MAKEFILE;

        #
        #   Add the generated data as a 'define'
        #   This will be placed early in the makefile so that it can be used
        #   by the rules that need it.
        #
        ToolsetDefine ($data);
    }
}

#-------------------------------------------------------------------------------
# Function        : ToolsetPostprocess
#
# Description     : Process any .RC files that have been specified
#                   by the user
#
# Inputs          : 
#
# Returns         : 
#
sub ToolsetPostprocess
{
    foreach my $rcfile ( @::RCSRCS )
    {
        my $name = StripDirExt($rcfile);
        my $base = StripDir($rcfile);
        my $res_name = '$(OBJDIR)/' . $name . '.res';

        #
        #   Create rules and recipes to build file
        #
        my $me = MakeEntry::New (*MAKEFILE, $res_name );
        $me->AddComment ("Create Resource File: $name" );
        $me->AddDependancy ( $rcfile );
        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
        $me->AddDependancy ( '$(GBE_OBJDIR)' );
        $me->AddDependancy ( split( /$;/, $::SRC_DEPEND{$base} ) ) if exists($::SRC_DEPEND{$base});
        $me->AddRecipe ( '$(DRES)' );
        $me->Print();

        #
        #   Add the file to the list of generated files
        #   This will ensure that its created early
        #
        GenerateSrcFile( 1, $res_name );
    }
}

#-------------------------------------------------------------------------------
# Function        : ToolsetAR
#
# Description     : Toolset processing to create a static library
#                   In Delphi these are called a unit
#                   In Delphi there are several restrictions
#                       * Name of the library and the name of the source are
#                         fixed - the user cannot provide a source name
#
#                       * A library can contain only one element
#
#
# Inputs          : $name   - Base name of the library
#                   $pArgs  - Ref to an array of arguments
#                   $pObjs  - Ref to an array of objects
#                   $pLib   - Ref to complete library entry
#
# Returns         : 
#

sub ToolsetAR
{
    my( $name, $pArgs, $pObjs, $pLib ) = @_;

    #.. Parse arguments
    #
    foreach $_ ( @$pArgs )
    {
        Message( "AR: unknown option $_ -- ignored" );
    }

    #
    #   Ensure that the user has not provided a source
    #   The name of the source will be calculated from the target
    #
    Error( "Delphi7 toolset: User source file names not supported")
        if ( scalar @$pObjs );

    #
    #   Source the required source file
    #   It must exist
    #
    my $sfile = MakeSrcResolve ( "$name.pas" );
    Error ("AR: Required source file not found: $sfile")
        unless ( -f $sfile );
    my $lib_name = $pLib->getPath();

    #
    #   Rule to create the target
    #
    my $me = MakeEntry::New (*MAKEFILE, $lib_name );
    $me->AddComment ("Build Unit: $name as a DCU" );
    $me->AddDependancy ( $sfile );
    $me->AddDependancy ( '$(SCM_MAKEFILE)' );
    $me->AddDependancy ( '$(DELPHISRCS)' );
    $me->AddRecipe ( '$(DCC_AR)' );
    $me->AddDefn ('PSOURCE', $sfile );
    $me->Print();
}

###############################################################################
#   ToolsetSHLD( $name, \@args, \@objs, \@libraries, $ver )
#       This subroutine takes the user options and builds the rules
#       required to link the shared library 'name'.
#
#   Arguments:
#       $name       - Name of the target program
#       $pArgs      - Ref to an array of argumennts
#       $pObjs      - Ref to an array of object files
#       $pLibs      - Ref to an array of libraries
#       $ver        - Version String
#
#   Output:
#       Makefile recipes to create the Program
#
#   Notes:
#       This Program Builder will handle its own dependancies
#       It will also create rules and recipes to construct various
#       parts directly from source
#
#       In Delphi there are several restrictions
#           * Name of the program and the name of the source are
#             linked. The user can provide only one file
#
#   Options:
#       --NoImplib                      # Supress creation of Import Library
#       --Package                       # Force Package Mode
#       Source File                     # Dependency usage only
#
#
###############################################################################

sub ToolsetSHLD
{
    my ( $name, $pArgs, $pObjs, $pLibs, $ver ) = @_;
    my @psource;
    my $no_implib = 0;
    my $auto_type = '';

    #.. Parse arguments
    #
    foreach ( @$pArgs ) {
        if (/^--NoImplib$/) {
            $no_implib = 1;

        } elsif ( /^--DCU/i ) {
            $auto_type = 'dcu';

        } elsif ( /^--Package/i ) {
            $auto_type = 'pkg';

        } elsif ( /^--NoPackage/i || /^--DLL/i ) {
            $auto_type = 'dll';
            
        } elsif ( !/^-/ ) {
            push @psource, MakeSrcResolve($_);

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

        }
    }

    #
    #   Objs are not supported in this toolset
    #
    Error( "Delphi7 toolset: User source file names not supported")
        if ( scalar @$pObjs );
    

    #
    #   Determine the source project name
    #   Will have the same name as the EXE, but the suffix may be
    #   .dpr or .pas
    #
    my $source;
    $::ScmQuiet = 3;                                # Can be done better !
    foreach my $suffix ( '.dpk', '.dpr', '.pas' )
    {
        $source = MakeSrcResolve ( "$name$suffix" );
        last if ( -f $source );
        $source = undef;
    }
    $::ScmQuiet = 0;

    Error ("Shared Library source not found",
           "It must have the same name as the library: $name")
            unless ( $source  );

    #
    #   Determine the type of SharedLib being created
    #       Library that creates DLLs
    #       Library that creates a Delphi Package
    #
    #   User can force the mode
    #
    unless ( $auto_type )
    {
        $auto_type = 'pkg' if ($source =~ m~\.dpk~);
        $auto_type = 'dcu' if ($source =~ m~\.pas~);
    }

    if ( $auto_type eq 'pkg' )
    {
        #
        #   Determine the target output name
        #
        my $base = $name;
        my $root = "\$(LIBDIR)/$base";
        my $bpl = $root . '.bpl';
        my $bcp = $root . '.dcp';

        #
        #   Create Rules and Recipes to create the Package
        #
        my $me = MakeEntry::New (*MAKEFILE, $bpl );
        $me->AddComment ("Build Delpi Package: $name" );
        $me->AddName ( $bcp );
        $me->AddDependancy ( $source );
        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
        $me->AddDependancy ( '$(DELPHISRCS)' );
        $me->AddDependancy ( @psource );
        $me->AddRecipe ( '$(SHDCC)' );
        $me->AddDefn ('SHLIBBASE', $base );
        $me->AddDefn ('PSOURCE', $source );
        $me->AddDefn ('PFLAGS',  '' );
        $me->AddDefn ('PACKAGES',  join ' ', @$pLibs );
        $me->Print();

        #
        #   Files to clean up
        #
        ToolsetGenerate( "$root.map" );
        ToolsetGenerate( "$base.drc" );                 # Created in CWD

        #
        #   Specify the files to be packaged as part of the shared library
        #
        PackageShlibAddFiles ( $name, $bpl );
        PackageShlibAddFiles ( $name, $bcp );
        PackageShlibAddFiles ( $name, "$root.map", "Class=map" );
    }
    elsif ( $auto_type eq 'dcu' )
    {
        #
        #   Really building a DCU
        #   Done as a shared lib so that it can access other packages
        #   with the -L option
        #
        my $base = $name;
        my $root = "\$(LIBDIR)/$base";
        my $dcu = $root . '.dcu';
        
        #
        #   Create Rules and Recipes to create the Package
        #
        my $me = MakeEntry::New (*MAKEFILE, $dcu );
        $me->AddComment ("Build Delpi Package: $name" );
        $me->AddDependancy ( $source );
        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
        $me->AddDependancy ( '$(DELPHISRCS)' );
        $me->AddDependancy ( @psource );
        $me->AddRecipe ( '$(DCC_AR)' );
        $me->AddDefn ('PSOURCE', $source );
        $me->AddDefn ('PFLAGS',  '' );
        $me->AddDefn ('PACKAGES',  join ' ', @$pLibs );
        $me->Print();

        #
        #   Files to clean up
        #
        ToolsetGenerate( "$base.drc" );                 # Created in CWD

        #
        #   Specify the files to be packaged as part of the shared library
        #
        PackageShlibAddFiles ( $name, $dcu );

    }
    else
    {
        #
        #   Determine the target output name
        #
        my $base = $name;
        my $root = "\$(LIBDIR)/$base";
        my $full = $root . '.' . $::so;
        my $stub = $root . '.lib';

        #
        #   Create Rules and Recipes to create the Shared Library
        #
        my $me = MakeEntry::New (*MAKEFILE, $full );
        $me->AddComment ("Build Shared Library: $name" );
        $me->AddDependancy ( $source );
        $me->AddDependancy ( '$(SCM_MAKEFILE)' );
        $me->AddDependancy ( '$(DELPHISRCS)' );
        $me->AddDependancy ( @psource );
        $me->AddRecipe ( '$(SHDCC)' );
        $me->AddDefn ('SHLIBBASE', $base );
        $me->AddDefn ('PSOURCE', $source );
        $me->AddDefn ('PFLAGS',  '' );
        $me->AddDefn ('PACKAGES',  join ' ', @$pLibs );
        $me->Print();

        #
        #   Files to clean up
        #
        ToolsetGenerate( "$root.map" );
        ToolsetGenerate( "$base.drc" );                 # Created in CWD

        #
        #   Specify the files to be packaged as part of the shared library
        #
        PackageShlibAddFiles ( $name, $full );
        PackageShlibAddFiles ( $name, "$root.map", "Class=map" );


        #
        #   Shared libaries are really need an import library
        #   Create an import library
        #
        unless ( $no_implib )
        {
            #
            #   Need:
            #       impdef.exe from Borland Cbuilder builder
            #       link.exe from MS VC6
            #
            my $me = MakeEntry::New (*MAKEFILE, $stub );
            $me->AddComment ("Build Import Library: $name" );
            $me->AddDependancy ( $full );
            $me->AddRecipe ( '$(IMPLIB)' );
            $me->AddDefn ('TMPFILE', "$root.tmp" );
            $me->Print();

            ToolsetGenerate( "$root.tmp" );
            ToolsetGenerate( "$root.exp" );
            PackageShlibAddFiles ( $name, $stub );
        }
    }
}
    
###############################################################################
#   ToolsetLD( $name, \@args, \@objs, \@libraries )
#       This subroutine takes the user options and builds the rules
#       required to link the program 'name'.
#
#   Arguments:
#       $name       - Name of the target program
#       $pArgs      - Ref to an array of argumennts
#       $pObjs      - Ref to an array of object files
#       $pLibs      - Ref to an array of libraries
#
#   Output:
#       Makefile recipes to create the Program
#
#   Notes:
#       This Program Builder will handle its own dependancies
#       It will also create rules and recipes to construct various
#       parts directly from source
#
#       In Delphi there are several restrictions
#           * Name of the program and the name of the source are
#             linked. The user can provide only one file
#
#   Options:
#       --Console                       # Console app
#       --Windows                       # Windows app (default)
#       Source File                     # Dependency usage only
#
#
###############################################################################

sub ToolsetLD
{
    my ( $name, $pArgs, $pObjs, $pLibs ) = @_;
    my @psource;
    my $link_target = $::ScmCompilerOpts{'LDSUBSYSTEM'};

    #.. Parse arguments
    #
    foreach ( @$pArgs ) {
        if (/^--Windows/) {
            $link_target = '-CC';

        } elsif (/^--Console/) {
            $link_target = '-CG';

        } elsif ( !/^-/ ) {
            push @psource, MakeSrcResolve($_);

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

        }
    }

    #
    #   Objs are not supported in this toolset
    #
    Error( "Delphi7 toolset: User source file names not supported")
        if ( scalar @$pObjs );

    #
    #   Determine the target output name
    #
    my $base = $name;
    my $root = "\$(BINDIR)/$base";
    my $full = $root . $::exe;

    #
    #   Determine the source project name
    #   Will have the same name as the EXE, but the suffix may be
    #   .dpr or .pas
    #
    my $source;
    $::ScmQuiet = 3;                                # Can be done better !
    foreach my $suffix ( '.dpr', '.pas' )
    {
        $source = MakeSrcResolve ( "$name$suffix" );
        last if ( -f $source );
        $source = undef;
    }
    $::ScmQuiet = 0;

    Error ("Program source not found",
           "It must have the same name as the program: $name")
            unless ( $source  );

    #
    #   Create Rules and Recipes to create the Program
    #   This will be a combination of source
    #
    my $me = MakeEntry::New (*MAKEFILE, $full );
    $me->AddComment ("Build Program: $name" );
    $me->AddDependancy ( $source );
    $me->AddDependancy ( '$(SCM_MAKEFILE)' );
    $me->AddDependancy ( '$(DELPHISRCS)' );
    $me->AddDependancy ( @psource );
    $me->AddRecipe ( '$(DCC)' );
    $me->AddDefn ('PSOURCE', $source );
    $me->AddDefn ('PFLAGS',  $link_target );
    $me->AddDefn ('PACKAGES',  join ' ', @$pLibs );
    $me->Print();

    #
    #   Files to clean up
    #
    ToolsetGenerate( "$root.map" );
    ToolsetGenerate( "$base.drc" );                 # Created in CWD


    #.. Package up files that are a part of the program
    #
    PackageProgAddFiles ( $name, $full );
    PackageProgAddFiles ( $name, "$root.map", "Class=map" );
}
#.. Successful termination
1;