Subversion Repositories DevTools

Rev

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

#! perl
########################################################################
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
#
# Module name   : jats_buildperl.pl
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): jats
#
# Description   : A utility to assist in building PERL Modules
#                 These are modules that need to be built via there
#                 own Makefile.PL - not to be confused with a JATS file
#                 of the same name.
#
#                 This script is intended to be called from within a JATS
#                 makefile.
#
# Usage:
#
#......................................................................#

require 5.006_001;
use strict;
use warnings;
use Cwd;
use FileUtils;
use JatsSystem;
use JatsError;
use ArrayHashUtils;
use File::Path;
use File::Copy;
use File::Find;
use Getopt::Long;
use Pod::Usage;                             # required for help support
use Config;

#
#   Initialise and configure some of the used packages
#
ErrorConfig( 'name' => 'BuildPerl' );
SystemConfig ( 'ExitOnError' => 1);
InitFileUtils();

#
#   Global variables
#
my $VERSION = "1.0.0";                      # Update this
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
my $opt_help = 0;
my $opt_manual = 0;
my $opt_test = 1;
my $opt_clean = 0;
my $opt_clean_build = 0;
my $opt_simple;

my @MAKE = 'make';
my $BASE = Getcwd();
my $PKG_DIR;
my @PACKAGES;
my %PCMDS;
my $GBE_MACHTYPE = '';
my @PERL5LIB_BASE = split( $ScmPathSep, $ENV{PERL5LIB});

#
#   Extract arguments
#
my $result = GetOptions (
                "help+"         => \$opt_help,              # flag, multiple use allowed
                "manual"        => \$opt_manual,            # flag, multiple use allowed
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
                "test!"         => \$opt_test,              # Flag, NoFlag
                "clean!"        => \$opt_clean,             # Flag, NoFlag
                "PackageDir=s"  => \$PKG_DIR,               # String
                "PerlPackage=s" => \@PACKAGES,              # String
                "MachType=s"    => \$GBE_MACHTYPE,          # String
                "clean_build"   => \$opt_clean_build,       # Flag
                "simple"        => \$opt_simple,            # Flag
                );

#
#   Process help and manual options
#
pod2usage(-verbose => 0, -message => "Version: $VERSION") if ($opt_help == 1 || ! $result);
pod2usage(-verbose => 1) if ($opt_help == 2 );
pod2usage(-verbose => 2) if ($opt_manual || ($opt_help > 2));
pod2usage(-verbose => 0, -message => "Version: $VERSION") if ( $#ARGV >= 0 );

#
#   Sanity Test user input
#
Error ("No output directory specified") unless ( $PKG_DIR );
Error ("No Perl Packages to build" ) unless ( @PACKAGES );
Error ("No GBE_MACHTYPE specified" ) unless ( $GBE_MACHTYPE );

#
#   Packaging directories
#       $PKG_DOC    - Where to place copyright notices
#       $PKG_PERL   - Where to place perl output
#
my $PKG_DIR_ABS  = FullPath( $PKG_DIR );
my $PKG_DOC = "$PKG_DIR_ABS/doc";
my $PKG_PERL= "$PKG_DIR_ABS/pkg/pkg.$GBE_MACHTYPE";

#
#   Process PACKAGES and extract the package options
#
my @PKGS;
for my $arg (@PACKAGES)
{
    my ($name,@options) = split( ',', $arg );
    UniquePush \@PKGS, $name;
    push @{$PCMDS{$name}}, @options;
}
@PACKAGES = @PKGS;
@PKGS = ();


Message    "======================================================================";
Message    "Build Perl using the native build method";
Message    "     GBE_MACHTYPE : $GBE_MACHTYPE";
Message    " Output Structure : " . ( $opt_simple ? 'Simple' : 'Legacy');
Message    "          Package : $PKG_DIR";
for my $ii (@PACKAGES)
{
    Message    "      PerlPackage : $ii";
}
Message    "======================================================================";

#
#   Sanity test the Perl Packages
#
for my $ii (@PACKAGES)
{
    Error ("Cannot file package: $ii") unless ( -d $ii );
    Error ("Perl Package does not contain Makefile.PL") unless ( -f "$ii/Makefile.PL" );
}

#
#   Remove some 'make' environment variables
#   The JATS make and the PACKAGE make must not know about each other
#
foreach my $var (qw ( MAKE MAKEFLAGS MAKEOVERRIDES MAKELEVEL MAKE_MODE CC CXX CPP ))
{
    delete $ENV{$var};
}

#
#   Windows specials
#
if ( ! $ENV{GBE_UNIX}   )
{
    #
    #   Must use nmake under windows
    #
    @MAKE = ('nmake', '-nologo', 'MAKE=nmake');

    #
    #   JATS sh fiddles with ComSpec under Windows
    #   Reinstate the original COMSPEC
    #
    $ENV{COMSPEC} =~ s~/~\\~g
}

if ($opt_verbose)
{
    push @MAKE, 'NOECHO='; 
}

#
#   Clean the build request
#   This will be invoked by a JATS clean
#
if ( $opt_clean_build )
{
    Message "Clean perl build directory";
    for my $ii (@PACKAGES)
    {
        if ( -f "$ii/Makefile" )
        {
            make( "Cleaning: $ii", $ii, 'clean' );
            unlink  "$ii/Makefile.new", "$ii/Makefile.old";
        }
    }
    exit 0;
}

#
#   Determine info to extend the library search path
#   Need to look in site, version and architecture specific directories
#   into which packages that we are building may have inserted modules
#
my @lib_search_path;
my $pversion = $Config{version};
my $parch = $Config{archname};

push @lib_search_path, "site_perl/$pversion/$parch";
push @lib_search_path, "site_perl/$pversion";
push @lib_search_path, "$pversion/$parch";
push @lib_search_path, "$pversion";

Verbose ('Perl Version List', @lib_search_path );

#
#   Create the Makefiles, build and install the artifacts
#
Message "Invoke package builder";
for my $ii (@PACKAGES)
{

    #
    #   Extend the PERL5LIB so that modules can find the packages that have already
    #   been built. This will satisfy any prerequisites.
    #
    #   Only use top-level directories in the package directory
    #   Don't use lower directories as this confuses the module loader
    #
    CalcLibPath();

    #
    #   Generate the Makefile
    #   Force installation into a known (and simple) structure
    #
    Message ("Building: $ii", @{$PCMDS{$ii}} );
    chdir $ii || Error( "Cannot change to directory: $ii");

    my @opts;
    if ($opt_simple)
    {
        push @opts,
            'INSTALLPRIVLIB=$(PREFIX)/share/perl',
            'INSTALLSITELIB=$(PREFIX)/share/perl',
            'INSTALLVENDORLIB=$(PREFIX)/share/perl',
            'INSTALLARCHLIB=$(PERLPREFIX)/lib/perl',
            'INSTALLSITEARCH=$(PREFIX)/lib/perl',
            'INSTALLVENDORARCH=$(PREFIX)/lib/perl',
            'INSTALLBIN=$(PERLPREFIX)/bin',
            'INSTALLSITEBIN=$(PREFIX)/bin',
            'INSTALLVENDORBIN=$(PREFIX)/bin',
            'INSTALLSCRIPT=$(PERLPREFIX)/bin',
            'INSTALLSITESCRIPT=$(PREFIX)/bin',
            'INSTALLVENDORSCRIPT=$(PREFIX)/bin',
            'INSTALLMAN1DIR=$(PERLPREFIX)/share/man/man1',
            'INSTALLSITEMAN1DIR=$(PREFIX)/share/man/man1',
            'INSTALLVENDORMAN1DIR=$(PREFIX)/share/man/man1',
            'INSTALLMAN3DIR=$(PERLPREFIX)/share/man/man3',
            'INSTALLSITEMAN3DIR=$(PREFIX)/share/man/man3',
            'INSTALLVENDORMAN3DIR=$(PREFIX)/share/man/man3';

    }

    System ( $ENV{GBE_PERL},  
            'Makefile.PL', 
            "PREFIX=$PKG_PERL", 
            @opts,
            @{$PCMDS{$ii}} );
    FixUpMakefile();
    chdir $BASE;

    rmtree("$ii/blib");
    unlink("$ii/pm_to_blib");

    #
    #   make all the default targets
    #
    make( "Building: $ii", $ii );
    make( "Installing: $ii", $ii, 'install' );
}

#
#   Test
#   Need to construct a PERL5LIB that will pick up all the .pm files
#
if ( $opt_test )
{
    Message "Calculate PERL5LIB for testing";
    CalcLibPath();
    Message("PERL5LIB: ", split( $ScmPathSep, $ENV{PERL5LIB} ) );

    Message "Testing Packages";
    for my $ii (@PACKAGES)
    {
        if ( -d "$ii/t" || -f "$ii/test.pl" )
        {
            make( "Testing: $ii", $ii, 'test' );
        }
        else
        {
            Message("No Tests found: $ii");
        }
    }
}

#
#   Transfer any COPYRIGHT files to the target package
#
Message "Transfer COPYRIGHT notice";
for my $ii (@PACKAGES)
{
    my $file = "$ii/COPYRIGHT";
    if ( -f $file )
    {
        my $package = StripDir ( $ii );
        my $target = "$PKG_DOC/$package";
        mkpath ( $target );
        unlink ( "$target/COPYRIGHT");
        copy( $file, $target ) || Error ( "Files not copied" );
    }
}

#
#   Cleanup the build output
#
if ( $opt_clean )
{
    Message "Clean build directory";
    for my $ii (@PACKAGES)
    {
        make( "Cleaning: $ii", $ii, 'clean' );
    }
}

Message "Script complete";
exit 0;


#-------------------------------------------------------------------------------
# Function        : FixUpMakefile
#
# Description     : Modify the generated Makefile to remove the doc_update recipe
#                   The doc_update step will mess with the perl installed on the
#                   build machine. This is not good.
#
# Inputs          :
#
# Returns         :
#
sub FixUpMakefile
{
    Message "Massage Makefile: remove doc_update";
    my $found = 0;
    open (MFH, '<', 'Makefile' )    || Error ("Cannot find generated Makefile");
    open (NFH, '>', 'Makefile.new') || Error ("Cannot create new Makefile");
    while ( <MFH> )
    {
        if ( m/^install ::/ )
        {
            $_ =~ s~doc_update~~g;
            $found = 1;
        }
        print NFH $_;
    }
    close MFH;
    close NFH;

}

#-------------------------------------------------------------------------------
# Function        : CalcLibPath
#
# Description     : Determine a suitale value for PERL5LIB to include
#                   directories in the package area
#
#                   Note: Do not use ALL subdirs in the package directory
#                         as a recusive devent of the pkg directory will confuse
#                         the module loader
#
#                   Use top level directories that contain .pm files
#
# Inputs          : None
#
# Returns         : Nothing
#                   Updates the EnvVar PERL5LIB
#
sub CalcLibPath
{
    my @dir_list =  grep -d $_ , glob( "$PKG_PERL/*" );
    my @lib_list;

    foreach my $dir ( @dir_list )
    {
        foreach my $subdir ( @lib_search_path )
        {
            push @lib_list, "$dir/$subdir"
                if ( -d "$dir/$subdir" );
        }
        push @lib_list, $dir;
    }

    $ENV{PERL5LIB} = join( $ScmPathSep, @lib_list, @PERL5LIB_BASE );
    Verbose ("PERL5LIB: ", split( $ScmPathSep, $ENV{PERL5LIB} ) );
}

#-------------------------------------------------------------------------------
# Function        : make
#
# Description     : Perform a 'make' in the required target directory
#
# Inputs          : $1  - Message
#                   $2  - Target directory
#                   $*  - Make arguments
#
# Returns         :
#
sub make
{
    my ($message, $dir, @args ) = @_;

    Message( $message ) if ( $message );

    chdir $dir || Error( "Cannot change to directory: $dir");
    System ( @MAKE , '-f', 'Makefile.new', @args );
    chdir $BASE;
}

1;

#-------------------------------------------------------------------------------
#   Documentation
#

=pod

=for htmltoc    MAKEUTIL::

=head1 NAME

jats_buildperl - Compile up a Perl (CPAN) Package

=head1 SYNOPSIS

perl jats_buildperl [options] (-PerlPackage=path)+

 Options:
    -help                       - brief help message
    -help -help                 - Detailed help message
    -man                        - Full documentation
    -[no]test                   - Test the packages
    -[no]clean                  - Clean the build area
    -PerlPackage=path[,options] - Path to the Perl Package to build
                                  Multiple packages are allowed
    -PackageDir=path            - Root of the output directory
    -GBE_MACHTYPE=name          - Type of the machine being used
    -Simple                     - SImple output directory structure

=head1 OPTIONS

=over 8

=item B<-help>

Print a brief help message and exits.

=item B<-help -help>

Print a detailed help message with an explanation for each option.

=item B<-man>

Prints the manual page and exits.

=item B<-[no]test>

This option may be used to disable the testing phase on all packages. The
default operation is to run the tests found in each package.

=item B<-[no]clean>

This option can be used to disable the process of cleaning the build area
after each build. The default option is to clean the build area.

=item B<-PerlPackage=path[,options]>

This option specified the path to a Perl Package. The program expects to find
the file Makefile.PL in this path.

The options, if present are provide to the Makefile.pl as arguments.

Multiple instances of the one path are compined and the 'options' are merged.
This allows multiple options to be specified.

Multiple packages can be built at the same time, by using multiple
-PerlPackage options. At least one package must be specified.

=item B<--PackageDir=path>

Mandatory option. This option specifies the root of the packaging directory.
The program will place the build artifacts within this directory.

=item B<-GBE_MACHTYPE=name>

Mandatory option. This option specifies the type of machine on which the package
is being built. This is used to package up the  build artifacts.

=item B<-Simple>

Recommended. This option will simplify the output directory structure and make it uniform 
between build platforms.

=back

=head1 DESCRIPTION

This program is used internally by Jats to implement the body of the MakePerlModule
directive. The command is not intended to be called by the user.

=head1 EXAMPLE

 MakePerlModule ('*', 'Crypt-Blowfish-2.10',
                      'Crypt-Blowfish_PP-1.12',
                      'Crypt-CBC-2.22',
                      '-notest' );

=cut