/usr/local/CPAN/IBM-LoadLeveler/Makefile.PL


use 5.006;
use ExtUtils::MakeMaker;

my $LLVER="UNKNOWN";
my $LDflags="";

$CC=$ExtUtils::MakeMaker::Config{"cc"};

#
# I hope this bit works out the base LoadLeveler version.
# Can't use the variables in the llapi.h file because the version number is the
# same for 3.1 & 3.2!  I am sure there is a very good reason for this, I don't know
# what it is, but i'm sure it must be a very good reason.
#

if ( $^O eq "linux" )
{
    $LoadL="/opt/ibmll/LoadL/full";
    # Quit if no llapi.h file (for CPAN testers)
    if ( ! -f "$LoadL/include/llapi.h")
    {
		print STDERR "Unable to find LoadLeveler include file\n";
		exit 0;
    }
    #$ExtraLibs="-lstdc++"; # stops load failure with error undefined symbol: __gxx_personality_v0
    $LLVER=llver();
    if ($LLVER eq "UNKNOWN" )
    {
    	#
	# If getting the version from llstatus -v failed, try the old way
	# of using the package information in the rpm database
	#
        # Using rpm information is a hack, you can get something like:
    	#
    	# LoadL-full-SLES10-X86-3.5.0.1-0
    	# LoadL-full-SLES10-X86_64-3.5.1.2-0
    	# LoadL-full-license-SLES10-X86-3.5.0.1-0
    	#
    	# So the loop searches for the highest version and uses that
    	# 
        my $rpm = "";
    	$rpm = "/usr/bin/rpm" if -f "/usr/bin/rpm";
    	$rpm = "/bin/rpm"     if -f "/bin/rpm";
    	my @packages=`$rpm -qa`;
    
    	foreach $package (@packages)
    	{
			next unless $package=~/LoadL.*-([\d.]+)-/;
			#print "$package ----> $1\n";
			my $ver=string_to_num($1);
			$LLVER=$ver if $ver > $LLVER;
    	}
    	if ( ! $LLVER eq "UNKNOWN" )
    	{
		print STDERR "Unable to extract LoadLeveler version\n";
		exit 0;
    	}
    }
    # 64 bit required on PowerPC
    if ( ! defined $ExtUtils::MakeMaker::Config{"use64bitall"} && $ExtUtils::MakeMaker::Config{"archname"} =~ /ppc-linux/)
    {
    	print "I'm not totally sure but I think you are trying to build with a 32 bit version of Perl, the llapi is 64 bit\n";	
		print "This is probably going to end very badly. Sorry\n\n";
    }
}
elsif  ( $^O eq "aix" )
{
    $LoadL="/usr/lpp/LoadL/full";
    # Quit if no llapi.h file (for CPAN testers)
    if ( ! -f "$LoadL/include/llapi.h")
    {
		print STDERR "Unable to find LoadLeveler include file\n";
		exit 0;
    }
    $ExtraLibs="";
    $LDflags="-b32";
    $LLVER=llver();
    if ($LLVER eq "UNKNOWN" )
    {
	    #
	    # The code extracts the package version using lslpp.
	    #
    	open(LLVER,"/usr/bin/lslpp -Ou -qlc LoadL.full |");
    	$_=<LLVER>;
    	$_=~/^[^:]+:[^:]+:([^:]+):/;
    	close(LLVER);
    	$LLVER=string_to_num($1);
    }
}
else
{
    # Return a value that CPAN testers might like
    # http://cpantest.grango.org/cgi-bin/pages.cgi?act=wiki-page&pagename=CPANAuthorNotes
    print STDERR "OS unsupported\n";
    print STDERR "LoadLevler is only supported on Linux and AIX. Found $^O\n";
    exit 0;
}

print "Building for version $LLVER on $^O\n\n";

process_defs();

WriteMakefile(
    'NAME'		     => 'IBM::LoadLeveler',
    'VERSION_FROM'	 => 'LoadLeveler.pm',
    'PREREQ_PM'		 => {}, # e.g., Module::Name => 1.1
    ($] >= 5.005 ?    ## Add these new keywords supported since 5.005
      (ABSTRACT_FROM => 'LoadLeveler.pod', # retrieve abstract from module
       AUTHOR        => 'Mike Hawkins <mike\@pink-pit.com>') : ()),
       'PM'          => { 'llapi.ph'        => '$(INST_LIBDIR)/llapi.ph',
		     	  'LoadLeveler.pm'  => '$(INST_LIBDIR)/LoadLeveler.pm'
			   },		   
    'MAN3PODS'	     => { 'LoadLeveler.pod' => '$(INST_MAN3DIR)/LoadLeveler.$(MAN3EXT)',
			  'DataAccess.pod'  => '$(INST_MAN3DIR)/LoadLeveler::DataAccess.$(MAN3EXT)',
			  'Submit.pod'      => '$(INST_MAN3DIR)/LoadLeveler::Submit.$(MAN3EXT)',
			  'Query.pod'       => '$(INST_MAN3DIR)/LoadLeveler::Query.$(MAN3EXT)',
			  'Workload.pod'    => '$(INST_MAN3DIR)/LoadLeveler::Workload.$(MAN3EXT)',
			  'Reservation.pod' => '$(INST_MAN3DIR)/LoadLeveler::Reservation.$(MAN3EXT)',
			  'Error.xsh'       => '$(INST_MAN3DIR)/LoadLeveler::Error.$(MAN3EXT)',
			  'FairShare.xsh'   => '$(INST_MAN3DIR)/LoadLeveler::Fairshare.$(MAN3EXT)',
			   },
    'LIBS'	=> ["-L$LoadL/lib -lllapi $ExtraLibs"], # e.g., '-lm'
    'DEFINE'	=> "-DLLVER=$LLVER",
    'INC'	=> "-I$LoadL/include",
    'OBJECT'	=> '$(O_FILES) ',
    'MYEXTLIB'  => 'ct.a',  # Fake Library to con MakeMaker into building C tests
    'CC'        => $CC,
    'macro'	=> { LoadL => "$LoadL" },
    'clean'     => { FILES => "defs.h llapi.ph ct.a ct/version ct/data_access ct/int_types *.html" },
    'depend'    => { "LoadLeveler.c" => "Reservation.xsh Configuration.xsh Workload.xsh Error.xsh FairShare.xsh Query.xsh Submit.xsh DataAccess.xsh" },
    'dynamic_lib' => { OTHERLDFLAGS => $LDflags }
);
sub MY::postamble {
    my $postamble = <<'END';
llapi.ph: $(LoadL)/include/llapi.h
	( cd $(LoadL)/include ; h2ph -d /tmp llapi.h )
	cat /tmp/llapi.ph | sed -e /require.*/d > $(INST_LIB)/../../llapi.ph
	rm /tmp/llapi.ph
html: LoadLeveler.pm DataAccess.pod Reservation.pod Submit.pod Query.pod Workload.pod Error.xsh FairShare.xsh LoadLeveler.pod Configuration.xsh
	pod2html --flush --noindex --podpath=. --htmlroot=. --outfile=DataAccess.html DataAccess.pod
	pod2html --noindex --podpath=. --htmlroot=. --outfile=Reservation.html Reservation.pod
	pod2html --noindex --podpath=. --htmlroot=. --outfile=Submit.html Submit.pod
	pod2html --noindex --podpath=. --htmlroot=. --outfile=Query.html Query.pod
	pod2html --noindex --podpath=. --htmlroot=. --outfile=Workload.html Workload.pod
	pod2html --noindex --podpath=. --htmlroot=. --title="Error Handling API" --outfile=Error.html Error.xsh
	pod2html --noindex --podpath=. --htmlroot=. --title "Fair Share Scheduling API" --outfile=FairShare.html FairShare.xsh
	pod2html --noindex --podpath=. --htmlroot=. --outfile=LoadLeveler.html LoadLeveler.pod
	pod2html --noindex --podpath=. --htmlroot=. --title="Configuration API" --outfile=Configuration.html Configuration.xsh
ct.a:	ct/version ct/data_access ct/int_types
	ar r ct.a	
ct/version: ct/version.c
	$(CC) -o $@ $(CCFLAGS) $(INC) $@.c $(EXTRALIBS) 	
ct/data_access: ct/data_access.c
	$(CC) -o $@ $(CCFLAGS) $(INC) $@.c $(EXTRALIBS) 	
ct/int_types: ct/int_types.c
	$(CC) -o $@ $(CCFLAGS) $(INC) $@.c $(EXTRALIBS) 	
END
    $postamble;
}

sub process_defs
{

    my %enums=();
    my @values=();

    print "Building list of current ll_get_data specifications\n";
    open(LLAPI,"$LoadL/include/llapi.h") || die "Can't open $LoadL/include/llapi.h $!";
    # Speed through the file till we find
    # the LLAPI_Specification enum
    while ( <LLAPI> )
    {
		last if /LLAPI_Specification/;
    }
    my $count=0;
    while ( <LLAPI> )
    {
		last if /}/;

		if ( /Unused_LL/ )
		{
			$count++;
			next;
		}
		next unless /LL/;
		chomp;
		# Don't bother with anything that doesn't have a comma in it.
		next unless /([^,]*),(.*)/;

		my $before=$1;
		my $after=$2;

		# If the version of C we are using does not support Designated Initializers
		# we need the enum id number. boo hiss btw
		$count = $1 if /=\s*(\d+),/;

		# Process the enum
		# remove leading whitespace
		$before=~s/^\s+//;
		# remove everything after a white space
		$before=~s/\s+.*//;
		# remove everything after and including an =
		$before=~s/=.*//;
	
		# Process the definition

		$after="NONE" if $after=~/^\s*$/;

		# remove anything up to the start of a type def
		$after=~s|^.*/\*\s*||;
		# remove everything after a :
		$after=~s|:.*||;
		# or a (
		$after=~s|\(.*||;
		# remove anything after a *
		$after=~s|\*\s+.*|\*|;
		# strip out C end comments
		$after=~s|\*/||;
		$after= uc($after);
		$after=~s/\s*$//g;
		$after=~s/\*/_STAR/g;
		$after=~s/ /_/g;
		$after=~s/_+/_/g;
		$after="LL_$after";
		#print "$count $before  -> $after  ($_)\n";

		$enums{$before}=$after;
		$values[$count++]=$before;
    }
    close(LLAPI);

    print "Generating new definitions array\n";
    open(DEFS,"defs.h.in") || die "Can't open defs.h.in $!";
    open(OUT,"> defs.h.new")|| die "Can't open defs.h.new $!";

    #
    # C99 introduced Designated Initalizers for arrays, meaning you
    # can do things like:
    # static int defs[]={
    #       [LL_StepCheckpointing] = LL_BOOLEAN_STAR,
    #       [LL_MachineName] = LL_CHAR_STAR,
    #                   };
    # Which is very nice since the alternative is to build a very nasty array.
    #
    # For now I don't know how to work out if C supports this feature, I do know it
    # doesn't work on my version of AIX though...
    #
    my $UseDesignatedInitializers = 0;

    $UseDesignatedInitializers = 0 if $^O eq "aix";

    # Copy everything out until we get to the array definition
    while( <DEFS> )
    {
		print OUT $_;
		last if ( $_ =~ /\[\]/ );
    }

    #
    # We are now pointing at the definitions in the template array
    # What I want to do at this point is to read the defs.h.in file
    # and if I find a line like
    #       [LL_StepCheckpointing] = LL_BOOLEAN_STAR,
    # then see if the LL_ thingie was defined in the
    # include file, if it is write it out, if not don't.
    #
    # However this will only work if the Designated Initializers
    # are supported, otherwise a fully defined array needs to be built
    #
    # So, suck my handcranked defs in from the defs.h.in file
    # and then loop through all enums as slurped out of the
    # system llapi.h file, @values contains a number to LL_ thingie
    # which can be used to see if there is anything to print.
    #

    my %defs=();
    while ( <DEFS> )
    {
		last if ( $_ !~ /\[.*\]/ );
		# slurp the definition from the file into a hash
		next unless $_=~/\[(.*)\]\s*=\s*(\S+)\s*\,/;
		$defs{$1}=$2;
    }

    for(my $i=0;$i<$count;$i++)
    {
		if ( exists $defs{$values[$i]})
		{
		    if ( $UseDesignatedInitializers)
	    	{
				print OUT "\t\[$values[$i]\] = $defs{$values[$i]},\n";
			#	print "\t\[$values[$i]\] = $defs{$values[$i]},\n";
	    	}
	    	else
	    	{
				print OUT "$defs{$values[$i]},\n";
	    	}
	    	delete $enums{$values[$i]};
	    	delete $defs{$values[$i]};
		}
		else
		{
	    	print OUT "LL_NONE,\n" if ! $UseDesignatedInitializers;
		}
    }

    # Close the array
    print OUT "};\n";
    print OUT "\n";
    close(OUT);
    close(DEFS);


    if  ( scalar keys %enums > 0 )
    {
		print "Current llapi.h has the following definitions, add them to defs.h at your own risk:\n\n";
		foreach my $key ( keys %enums)
		{
		    print "[$key] = $enums{$key},\n";
		}
		print "\n";
    	}
    	if  ( scalar keys %defs > 0 )
    	{
		print "defs.h.in has the following definitions not seen in your system llapi.h file.  They have been removed from defs.h:\n\n";
		foreach my $key ( keys %defs)
		{
		    print "[$key] = $defs{$key},\n";
		}
		print "\n";
    }

    if ( -s "defs.h.new" )
    {
		unlink "defs.h.old" if ( -f "defs.h.old");
		rename "defs.h","defs.h.old" if ( -f "defs.h" );
		rename "defs.h.new","defs.h";
    }
}

#
# Try and use llstatus to determine version
#

sub llver
{
	my $version = "UNKNOWN";
	
	if ( -x "$LoadL/bin/llstatus" )
	{
		my $status=`$LoadL/bin/llstatus -v`;
		$version=string_to_num($1) if $status=~/\S+\s+(\S+)/;
	}
	return $version;
}

#
# The code converts the package version to a number, eg
# String is 3.1.0.16
# 	doubles any single digits:      03.01.00.16
#       removes the dots:               03010016
#	removes the leading 0 (octal):  3010016
# The final number is used in #ifdef statements
#
# The 1 while ..... construct repeatedly runs the doubling search. This is needed
# because if you match one .0. then the start for the next search is the first digit
# in the next number. See Page 155 of Camel V3. Alternativly just trust me that it works :)
#

sub string_to_num
{
	my $version = shift;
	
	1 while $version=~s/\.(\d)\./.0$1./g;
	$version=~s/\.(\d)$/.0$1/;
	$version=~s/\.//g;
	$version=~s/^0(\d+)/$1/;
	
	return $version;
}