The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
################################################################################
#
#  Copyright 2011 Zuse Institute Berlin
#
#  This package and its accompanying libraries is free software; you can
#  redistribute it and/or modify it under the terms of the GPL version 2.0,
#  or the Artistic License 2.0. Refer to LICENSE for the full license text.
#
#  Please send comments to kallies@zib.de
#
################################################################################
#
# Makefile for Sys::Hwloc
#
# $Id: Makefile.PL,v 1.24 2011/01/11 10:49:39 bzbkalli Exp $
#
################################################################################

BEGIN { require 5.006; }

use ExtUtils::MakeMaker;
use Config;
use Cwd;

%INFOS   = (
	    NAME               => 'Sys::Hwloc',
	    VERSION_FROM       => 'lib/Sys/Hwloc.pm',
	    AUTHOR             => 'Bernd Kallies <kallies@zib.de>',
	    LICENSE            => 'perl',
	    ABSTRACT           => 'Perl wrapper around the hwloc API',
	    PREREQ_PM          => {
				   'Test::More' => '0.89',
				  },
	    CONFIGURE_REQUIRES => {
				   'ExtUtils::Constant' => 0,
				  },
	   );

# ------------------------------------------------------------------------------
# Configure using pkg-config
# ------------------------------------------------------------------------------

%CONFIGS = (
	    config            => {
				  'prefix'      => '',
				  'exec_prefix' => '',
				  'libdir'      => '',
				  'includedir'  => '',
				  'iflags'      => '',
				  'libs'        => '',
				  'define'      => '',
				 },
	    HWLOC_API_VERSION => undef,
	    HWLOC_HAS_XML     => 0,
	   );

configure();

# ------------------------------------------------------------------------------
# Write makefile
# ------------------------------------------------------------------------------

delete $INFOS{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
delete $INFOS{LICENSE}            unless eval { ExtUtils::MakeMaker->VERSION(6.31) };

WriteMakefile(
 	      %INFOS,
 	      INC       => "$CONFIGS{config}->{iflags}",
	      DEFINE    => "$CONFIGS{config}->{define}",
              OPTIMIZE  => "$Config{optimize}",
	      LIBS      => "$CONFIGS{config}->{libs}",
	      PM_FILTER => "sed -e s/\@HWLOC_API_VERSION\@/$CONFIGS{HWLOC_API_VERSION}/",
	      depend    => {
			    "Hwloc.c" => "Hwloc.xs hwloc_cpuset.xsh hwloc_bitmap.xsh",
			   },
 	     );

if(eval { require ExtUtils::Constant; 1 }) {
  # If you edit these definitions to change the constants used by this module,
  # you will need to use the generated const-c.inc and const-xs.inc
  # files to replace their "fallback" counterparts before distributing your
  # changes.
  my @names = (
	       { name => 'HWLOC_OBJ_SYSTEM',                  macro => 1 },
	       { name => 'HWLOC_OBJ_MACHINE',                 macro => 1 },
	       { name => 'HWLOC_OBJ_NODE',                    macro => 1 },
	       { name => 'HWLOC_OBJ_SOCKET',                  macro => 1 },
	       { name => 'HWLOC_OBJ_CACHE',                   macro => 1 },
	       { name => 'HWLOC_OBJ_CORE',                    macro => 1 },
	       { name => 'HWLOC_OBJ_MISC',                    macro => 1 },
	       { name => 'HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM', macro => 1 },
	       { name => 'HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM',  macro => 1 },
	       { name => 'HWLOC_TYPE_DEPTH_MULTIPLE',         macro => 1 },
	       { name => 'HWLOC_TYPE_DEPTH_UNKNOWN',          macro => 1 },
	       { name => 'HWLOC_TYPE_UNORDERED',              macro => 1 },
	       { name => 'HWLOC_HAS_XML',                     macro => 1 },
	       { name => 'HWLOC_CPUBIND_PROCESS',             macro => 1 },
	       { name => 'HWLOC_CPUBIND_THREAD',              macro => 1 },
	       { name => 'HWLOC_CPUBIND_STRICT',              macro => 1 },
	      );

  if(! $CONFIGS{HWLOC_API_VERSION}) {
    push @names, { name => 'HWLOC_OBJ_PROC',           macro => 1 };
  } else {
    push @names, { name => 'HWLOC_OBJ_GROUP',          macro => 1 };
    push @names, { name => 'HWLOC_OBJ_PU',             macro => 1 };
    if($CONFIGS{HWLOC_API_VERSION} ge '0x00010100') {
      push @names, { name => 'HWLOC_CPUBIND_NOMEMBIND',   macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_PROCESS',     macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_THREAD',      macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_STRICT',      macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_MIGRATE',     macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_NOCPUBIND',   macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_DEFAULT',     macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_FIRSTTOUCH',  macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_BIND',        macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_INTERLEAVE',  macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_REPLICATE',   macro => 1 };
      push @names, { name => 'HWLOC_MEMBIND_NEXTTOUCH',   macro => 1 };
    }
  }

  ExtUtils::Constant::WriteConstants(
                                     NAME         => 'Sys::Hwloc',
                                     NAMES        => \@names,
                                     DEFAULT_TYPE => 'IV',
                                     C_FILE       => 'const-c.inc',
                                     XS_FILE      => 'const-xs.inc',
                                  );

} else {
  use File::Copy;
  use File::Spec;
  foreach my $file ('const-c.inc', 'const-xs.inc') {
    my $fallback = File::Spec->catfile('fallback', $file);
    copy ($fallback, $file) or die "Can't copy $fallback to $file: $!";
  }
}

# ------------------------------------------------------------------------------
# Assemble %CONFIGS
# ------------------------------------------------------------------------------

sub configure {

  print "checking for hwloc... ";
  my $output = `pkg-config --exists --print-errors hwloc 2>&1`;
  if($?) {
    die "no\n$output";
  } else {
    print "yes\n";
  }

  print "checking for hwloc prefix... ";
  $CONFIGS{config}->{prefix} = pkg_config_get("variable=prefix");
  print "$CONFIGS{config}->{prefix}\n";

  print "checking for hwloc eprefix... ";
  $CONFIGS{config}->{eprefix} = pkg_config_get("variable=exec_prefix");
  print "$CONFIGS{config}->{eprefix}\n";

  print "checking for hwloc includedir... ";
  $CONFIGS{config}->{includedir} = pkg_config_get("variable=includedir");
  $CONFIGS{config}->{includedir} = "$CONFIGS{config}->{eprefix}/include" if(! $CONFIGS{config}->{includedir} && $CONFIGS{config}->{eprefix});
  $CONFIGS{config}->{includedir} = "$CONFIGS{config}->{prefix}/include"  if(! $CONFIGS{config}->{includedir} && $CONFIGS{config}->{prefix});
  print "$CONFIGS{config}->{includedir}\n";

  print "checking for hwloc libdir... ";
  $CONFIGS{config}->{libdir}  = pkg_config_get("variable=libdir");
  $CONFIGS{config}->{libdir}  = "$CONFIGS{config}->{eprefix}/lib" if(! $CONFIGS{config}->{libdir} && $CONFIGS{config}->{eprefix});
  $CONFIGS{config}->{libdir}  = "$CONFIGS{config}->{prefix}/lib"  if(! $CONFIGS{config}->{libdir} && $CONFIGS{config}->{prefix});
  print "$CONFIGS{config}->{libdir}\n";

  print "checking for hwloc -I flags... ";
  $CONFIGS{config}->{iflags} = pkg_config_get("cflags-only-I");
  print "$CONFIGS{config}->{iflags}\n";

  print "checking for hwloc libs... ";
  $CONFIGS{config}->{libs} = pkg_config_get("libs");
  print "$CONFIGS{config}->{libs}\n";

  print "checking if hwloc supports hwloc_get_api_version... ";
  if(hwloc_has_func("hwloc_get_api_version")) {
    print "yes\n";
    $CONFIGS{config}->{define} .= " -DHAVE_HWLOC_GET_API_VERSION";
  } else {
    print "no\n";
  }

  print "checking if hwloc supports XML... ";
  if(hwloc_has_func("hwloc_topology_export_xml")) {
    print "yes\n";
    $CONFIGS{HWLOC_HAS_XML} = 1;
  } else {
    print "no\n";
  }
  $CONFIGS{config}->{define} .= " -DHWLOC_HAS_XML=$CONFIGS{HWLOC_HAS_XML}";

  print "checking HWLOC_API_VERSION... ";
  my $cmd  = "$Config{cpprun} -include hwloc.h";
  $cmd    .= " -I$CONFIGS{config}->{includedir}" if $CONFIGS{config}->{includedir};
  $output  = `echo HWLOC_API_VERSION | $cmd $Config{cpplast}`;
  if($?) {
    die "Failed to determine HWLOC_API_VERSION\n";
  } else {
    (my $lastword) = ($output =~ /(\S+)$/);
    die "\nFailed to determine HWLOC_API_VERSION\n" unless defined $lastword;
    if($lastword ne "HWLOC_API_VERSION") {
      $CONFIGS{HWLOC_API_VERSION} = $lastword;
      print "$CONFIGS{HWLOC_API_VERSION}\n";
    } else {
      print "(undef)\n";
    }
  }

}

# ------------------------------------------------------------------------------
# Run pkg-config for hwloc package, retrieve some info
# ------------------------------------------------------------------------------

sub pkg_config_get {
  my $what = shift;
  my $cmd  = "pkg-config --$what hwloc";
  my $data = `$cmd`;
  die "$cmd failed.\n" if $?;
  $data =~ s/[\015\012]+$//;
  return $data;
}

# ------------------------------------------------------------------------------
# See if a function is present in libhwloc.
# Needs $Config{cc} to work.
# Needs $CONFIGS{config} to be set.
# Return 1 on success, 0 on error.
# ------------------------------------------------------------------------------

sub hwloc_has_func {
  my $func = shift;
  return 0 unless $func;

  my $startDir = cwd();
  my $testDir  = '.testlink';
  my $testFile = 't.c';

  rm_fr($testDir);
  mkdir($testDir, 0777) or die "Failed to create dir $testDir: $!";
  chdir($testDir);

  open(FILE, ">$testFile") or die "Failed to write to file $testFile: $!";
  print FILE <<EOT;
char $func ();
int main () { $func (); return 0; }
EOT
  close(FILE);
  `$Config{cc} $CONFIGS{config}->{libs} $testFile >/dev/null 2>&1`;
  my $rc = $?;

  chdir($startDir);
  rm_fr($testDir);

  return $rc ? 0 : 1;
}

# ------------------------------------------------------------------------------
# rm -fr
# ------------------------------------------------------------------------------

sub rm_fr {
  my @files     = @_;
  my @realFiles = ();
  foreach(@files) {
    push @realFiles, glob($_);
  }
  foreach(@realFiles) {
    if(-d $_) {
      rm_fr("$_/*");
      rmdir($_) or die "Failed to remove $_: $!";
    } else {
      chmod(0777, $_);
      unlink($_);
    }
  }
}