#*******************************************************************************
# Copyright (c) 1997-2007, Perforce Software, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#*******************************************************************************
use ExtUtils::MakeMaker;
use Getopt::Long;
use File::Copy;
use English;
use Cwd;
use Cwd 'abs_path';
use Config;
use strict;
#$ExtUtils::MakeMaker::Verbose = 1;
# This is the version of the API that this build of P4Perl supports.
our $TARGET_API_VERSION = encode_api_version( "2007.3" );
our $APIDIR = undef;
our $P4PORT = undef;
our $P4API_VERSION = undef;
our $P4PERL_VERSION = undef;
our $P4PERL_PATCHLEVEL = undef;
our $P4PERL_PLAT = undef;
our $P4PERL_OS = undef;
our $P4PERL_OSVER = undef;
our @P4PERL_DATE = undef;
our $RELNOTES_PATH = '../p4-doc/user/p4perlnotes.txt';
#
# Check the Perforce API version from the Version file
#
sub extract_api_version
{
my $path = shift;
my $cwd = getcwd();
foreach my $vfile ( "$path/Version", "$path/sample/Version" )
{
next unless ( -e $vfile );
open( VF, $vfile ) or die( "Can't open $vfile" );
while( <VF> )
{
if( /^RELEASE = (.*) ;/ )
{
$P4API_VERSION = $1;
$P4API_VERSION =~ s/ /./g;
last;
}
}
close( FH );
}
my $tries = 3;
while( !defined( $P4API_VERSION ) && --$tries )
{
print( "Unable to determine API version string\n" );
print( "Enter API version: " );
$P4API_VERSION = <STDIN>;
}
die( "Aborting: API version not provided" ) unless $P4API_VERSION;
printf( "Found %s Perforce API in %s\n", $P4API_VERSION, $path );
return $P4API_VERSION;
}
#
# Encode an API version string into an integer
#
sub encode_api_version( $ )
{
my $vs = shift;
if( defined $vs && $vs =~ /^(\d+)(\.|\s)(\d+)/ )
{
return (($1 << 8 ) | $3 );
}
return undef;
}
sub api_major( $ )
{
my $v = shift;
return $v >> 8;
}
sub api_minor( $ )
{
my $v = shift;
return $v & 0xff;
}
sub is_api_dir( $ )
{
my $dir = shift;
return 0 unless ( -d $dir );
return 1 if( -e "$dir/Version" );
return 1 if( -e "$dir/sample/Version" );
return 0;
}
#
# Check that they're using the API version we want.
#
sub check_api_version( $ )
{
my $api_ver = shift;
my $api_name = sprintf( "%d.%d", api_major( $TARGET_API_VERSION ),
api_minor( $TARGET_API_VERSION ) );
$api_ver = encode_api_version( $api_ver );
return 1 if( $api_ver == $TARGET_API_VERSION );
print <<EOF;
This version of P4Perl was designed for use with the $api_name release of
the Perforce C++ API. Using it with other releases may not work, and is
not supported.
EOF
print( "Do you wish to continue anyway? (y/n): " );
my $answer = <STDIN>;
chomp( $answer );
return 0 unless $answer eq "y";
return 1;
}
sub get_api_dir()
{
if( defined( $APIDIR ) && is_api_dir( $APIDIR ) )
{
return $APIDIR;
}
# Get the path to the Perforce API
my $apipath = undef;
my $tries = 3;
$apipath = $APIDIR if( defined( $APIDIR ) );
while( !defined( $apipath ) && --$tries )
{
print( "Enter the path to the Perforce API: " );
$apipath = <STDIN>;
$apipath =~ s/\n//;
# Filthy support for ~/ type paths ( NOT ~user/ though! )
$apipath =~ s#\~/#$ENV{HOME}/#;
$apipath = abs_path( $apipath );
$apipath = undef unless( is_api_dir( $apipath ) );
}
die ( "Aborting - no API directory provided" ) unless $apipath;
return $apipath;
}
sub define( $$;$$ )
{
my $href = shift;
my $var = shift;
my $val = shift;
my $string = shift;
if( !$val )
{
$href->{ 'DEFINE' } .= qq{ -D$var};
}
elsif( $string )
{
$href->{ 'DEFINE' } .= qq{ -D$var="\\"$val\\""};
}
else
{
$href->{ 'DEFINE' } .= qq{ -D$var="$val"};
}
}
#
# Add the Perforce libraries to the linker configuration
#
sub add_p4_libs( $$$ )
{
my $cfg = shift; # Perl's pre-set config
my $flags = shift; # Flags we want to add
my $apidir = shift; # Our API directory
my $libdir = $apidir;
$libdir = "$libdir/lib" if( -d "$libdir/lib" );
$flags->{'LIBS'} = [];
if( defined( $cfg->{LIBS} ) )
{
my $libs = $cfg->{LIBS};
foreach my $libset (@$libs )
{
push( @{$flags->{LIBS}},
"-L$libdir -lclient -lrpc -lsupp $libset" );
}
}
else
{
push( @{$flags->{LIBS}}, "-L$libdir -lclient -lrpc -lsupp" );
}
}
#
# Add the Perforce headers to the includes
#
sub add_p4_hdrs( $$ )
{
my $flags = shift;
my $apipath = shift;
$apipath = "$apipath/include/p4" if( -d "$apipath/include/p4" );
$flags->{ 'INC' } = "-I$apipath -Ilib";
}
sub identify_p4perl()
{
open( VF, "p4perl_version" ) or die( "Can't open p4perl_version file!" );
while( <VF> )
{
if( /^RELEASE\s*=\s*([[:alnum:] ]+)/ )
{
$P4PERL_VERSION = $1;
$P4PERL_VERSION =~ s/ +$//;
$P4PERL_VERSION =~ s/ +/./g;
}
elsif( /^PATCHLEVEL\s*=\s*(\d+)/ )
{
$P4PERL_PATCHLEVEL = $1;
}
elsif( /^SUPPDATE\s*=\s*([0-9 ]+)/ )
{
@P4PERL_DATE = split( / +/, $1 );
}
}
close( VF );
}
#
# Work out what platform we're on. What's in here is stuff that we have
# to do on all platforms - anything that's specific to one platform should
# go in its hints file.
#
sub identify_platform( $ )
{
my $href = shift;
# Read $Config{ 'archname' ), which looks like this:
#
# i86pc-solaris-64int (solaris10x86_64 )
# x86_64-linux-gnu-thread-multi (linux26x86_64)
# MSWin32-x86-multi-thread (ntx86) - note os/plat reversal!!!
#
# So, we get the osname from Config, then we use whichever of the
# first two fields in archname is NOT the os as the platform.
#
my $os = $Config{ 'osname' };
my @fields = split( /-/, $Config{ 'archname' } );
$P4PERL_PLAT = $fields[ 0 ] if( $os eq $fields[ 1 ] );
$P4PERL_PLAT = $fields[ 1 ] if( $os eq $fields[ 0 ] );
# Now convert the platform identifier to Perforce style for known
# differences in terminology
$P4PERL_PLAT = "x86" if( $P4PERL_PLAT =~ "[xi]86pc" );
$P4PERL_PLAT = "sparc" if( $P4PERL_PLAT =~ /sun/ );
$P4PERL_PLAT = uc( $P4PERL_PLAT );
# Now convert the OS to Perforce style for known differences.
$os = "NT" if( $os eq "MSWin32" );
# Default is our guestimate.
$P4PERL_OS = uc( $os );
# Identify the OS version. We take the first two numbers in the
# dotted string. On NT, we don't bother.
if( $os eq "NT" )
{
$P4PERL_OSVER="";
}
else
{
$P4PERL_OSVER = $Config{ 'osvers' };
$P4PERL_OSVER =~ s/^(\d+)\.(\d+).*/\1\2/;
}
#
# Override any derived values with hints from the hints file for the
# platform
#
if( defined( $href->{ 'P4PERL_OS_HINT' } ) )
{
$P4PERL_OS = $href->{ 'P4PERL_OS_HINT' };
delete( $href->{ 'P4PERL_OS_HINT' } );
}
if( defined( $href->{ 'P4PERL_PLAT_HINT' } ) )
{
$P4PERL_PLAT = $href->{ 'P4PERL_PLAT_HINT' };
delete( $href->{ 'P4PERL_PLAT_HINT' } );
}
if( defined( $href->{ 'P4PERL_OSVER_HINT' } ) )
{
$P4PERL_OSVER = $href->{ 'P4PERL_OSVER_HINT' };
delete( $href->{ 'P4PERL_OSVER_HINT' } );
}
}
#
# This sub adds the Perforce API path to the header includes and libs
# used by the compiler. It's called by WriteMakefile.
#
sub config_sub
{
my $class = shift;
my $href = shift;
# Work out what platform we're running on, and the P4Perl version
identify_platform( $href );
# Define Ident macros
my $id_os = $P4PERL_OS . $P4PERL_OSVER . $P4PERL_PLAT;
define( $href, "ID_OS", $id_os, 1 );
define( $href, "ID_REL", $P4PERL_VERSION, 1 );
define( $href, "ID_PATCH", $P4PERL_PATCHLEVEL, 1 );
define( $href, "ID_Y", $P4PERL_DATE[ 0 ], 1 );
define( $href, "ID_M", $P4PERL_DATE[ 1 ], 1 );
define( $href, "ID_D", $P4PERL_DATE[ 2 ], 1 );
# Define OS_* macros
define( $href, "OS_$P4PERL_OS" );
define( $href, "OS_${P4PERL_OS}${P4PERL_OSVER}" );
define( $href, "OS_${P4PERL_OS}${P4PERL_OSVER}${P4PERL_PLAT}" );
define( $href, "OS_${P4PERL_OS}${P4PERL_PLAT}" );
# Now find the API!
my $flags = {};
# Get the path to the Perforce API
my $apipath = get_api_dir(); # dies if API not found
extract_api_version( $apipath );
#
# Abort if the user's decided not to press ahead with a newer
# version of the API than this version was written for
#
exit( 1 ) unless ( check_api_version( $P4API_VERSION ) );
define( $href, "P4API_VERSION", encode_api_version( $P4API_VERSION ) );
define( $href, "ID_API", $P4API_VERSION, 1 );
add_p4_libs( $href, $flags, $apipath );
add_p4_hdrs( $flags, $apipath );
#
# Handle renaming the distribution to our standard filename
# convention
#
if( $P4PERL_OS eq 'NT' )
{
$flags->{ 'POSTOP' } =
"ren p4perl-${P4PERL_VERSION}.tar.gz p4perl.tar.gz";
}
else
{
$flags->{ 'POSTOP' } =
"mv -f p4perl-${P4PERL_VERSION}.tar.gz p4perl.tar.gz";
}
return $flags;
}
# Ensure that the clientuserperl interface gets built.
sub MY::postamble
{
'
$(MYEXTLIB): lib/Makefile
cd lib && $(MAKE) $(PASSTHRU)
';
}
#*******************************************************************************
#* START OF MAIN SCRIPT
#*******************************************************************************
identify_p4perl();
my %flags = (
'NAME' => 'P4',
'VERSION' => $P4PERL_VERSION,
'PREREQ_PM' => {}, # e.g., Module::Name => 1.1
'MYEXTLIB' => 'lib/libp4$(LIB_EXT)',
'XSOPT' => '-C++ -prototypes',
'CONFIGURE' => \&config_sub,
'DISTNAME' => 'p4perl',
'PL_FILES' => { },
'clean' => { 'FILES' => [ qw( p4perl.tar.gz ) ] },
);
my $result = GetOptions ( "apidir=s" => \$APIDIR,
"p4port=s" => \$P4PORT );
#
# Ensure that the release notes are copied in from the p4-doc
# tree if it exists. If not, we'll check that we have the release
# notes in place already and warn the user if that's not the case.
#
if( -f $RELNOTES_PATH )
{
printf( "Copying release notes from p4-doc tree...\n" );
copy( $RELNOTES_PATH, "RELNOTES.txt" );
# Remove RELNOTES.txt on 'make distclean'. This will cause
# a manifest check warning, but it keeps the tree tidy.
push( @{$flags{ 'clean' }->{ 'FILES' }}, "RELNOTES.txt" );
}
elsif( ! -f "RELNOTES.txt" )
{
warn( "The P4Perl release notes are missing." );
warn( "Proceeding anyway." );
}
#
# Now invoke MakeMaker to build the makefile
#
WriteMakefile( %flags );