#! perl 
use Cwd;
use Config;

#
# Do a perl check for version >= 5.005.  See 'gpt-translate-interpreter' 
# should you need to alter the invocation path to a valid perl interpreter in 
# the GPT front-end programs.
#

if ( ! ( defined eval "require 5.005" ) )
{
  die "GPT requires at least Perl version 5.005";
}

my $gpath = $ENV{GPT_LOCATION};
 
if (!defined($gpath))
{
  $gpath = $ENV{GLOBUS_LOCATION};
}
   
if (!defined($gpath))
{
  die "GPT_LOCATION or needs to be set before running this script";
}

@INC = ("$gpath/lib/perl", "$gpath/lib/perl/$Config{'archname'}", @INC);

system( "$gpath/sbin/gpt_version" ) == 0
         or die "GPT died due to Version mismatch.  Check PATH and GPT_LOCATION\n" ;

if ( !( defined eval "require Grid::GPT::GPTObject" ) )
{
  die("$gpath does not appear to hold a valid GPT installation\n");
}

require Pod::Usage;

my ($srcdir, $tmpdir, $nodeps, $outputfile, $all, @xmlBundle, $sdk, $native, 
    $help, $version, @excludes, $bundlename, $bundleversion, $arch, $nosetups, 
    $man, $workingdir, $bndlFile, $location, $bundlestab, $bundlelabel, 
    $bundledef, $verbose, $debug, $buildno, $rpmprefix, $rpmlicense, $keep);

GetOptions(
           'srcdir=s'              => \$srcdir,
           'tmpdir=s'              => \$tmpdir,
           'bindir=s'              => \$tmpdir,
           'installdir=s'          => \$location,
           'xml=s'                 => \@xmlBundle,
           'nodeps'                => \$nodeps,
           'nosetups'              => \$nosetups,
           'sdk'                   => \$sdk,
           'native'                => \$native,
           'keep-bin-pkgs|keep'    => \$keep,
           'output=s'              => \$outputfile,
           'bundlename|bn=s'       => \$bundlename,
           'bundleversion|bv=s'    => \$bundleversion,
           'bundlestability|bs=s'  => \$bundlestab,
           'bundlelabel|bl=s'      => \$bundlelabel,
           'exclude|x=s'           => \@excludes,
           'all'                   => \$all,
           'help|?'                => \$help,
           'man'                   => \$man,
           'version'               => \$version,
           'verbose'               => \$verbose, 
           'debug'                 => \$debug, 
           'buildnumber|buildno=i' => \$buildno,
           'rpmprefix=s'           => \$rpmprefix,
           'rpmlicense=s'          => \$rpmlicense,
           'template'              => \$template,
           'bundledef=s'           => \$bundledef,
           'location=s'            => \$location
          )
  or Pod::Usage::pod2usage(0);

Pod::Usage::pod2usage(0) if $help;
Pod::Usage::pod2usage(-verbose => 2) if $man;

require Grid::GPT::GPTIdentity;
Grid::GPT::GPTIdentity::print_gpt_version() if defined $version;

require Grid::GPT::PkgDist;
require Grid::GPT::Installation;
require Grid::GPT::PkgFileName;
require Grid::GPT::FilelistFunctions;
require Grid::GPT::Locations;
use Getopt::Long; 
require Archive::Tar;
require Grid::GPT::PkgMngmt::Inform;

#Functions Prototypes
sub cleandir; 
sub process_bundle;


my $log = new Grid::GPT::PkgMngmt::Inform( verbose => $verbose, 
                                           debug   => $debug );

my $locations = new Grid::GPT::Locations( installdir => $location,
                                          tmpdir     => $tmpdir,
                                          bindir     => $tmpdir,
                                          srcdir     => $srcdir );

my %pkgtype2depenv = (
                      'data'       => ['Runtime', 'Setup'],
                      'doc'        => ['Runtime', 'Setup'],
                      'dev'        => ['Build'],
                      'pgm'        => ['Runtime','Setup'],
                      'pgm_static' => ['Runtime', 'Setup'],
                      'rtl'        => ['Runtime', 'Setup'],
                      'sdk'        => ['Build'],
                      'src'        => ['Build', 'Runtime', 'Setup'],
                     );

my $dist;
my $bundlelist;
my $nativebundlelist;
my @deporderlist;
my @sortedpkgs;
my @sortedpkgslist;
my @nativesortedpkgs;
my $pkgdir;
my $preferred_flavor;
#
# format our exclude and type lists
#

my $bundleName        = undef;
my $bundleVersion     = undef;

my @exclude_queries;

my $parsenames = new Grid::GPT::PkgFileName( locations => $locations );

for my $e (@excludes) 
{
  push @exclude_queries, $parsenames->parse_name($e);
}

my @pkg_queries;

for my $p (@ARGV) 
{
  push @pkg_queries, $parsenames->parse_name($p);
}

##my $bndl              = new Grid::GPT::V1::Bundle;
my $bndl          = undef;

if( defined($template) )
{
  $bndl           = new Grid::GPT::V1::Bundle;

    create_bundle();
  create_template();
  exit 0;
}

if( @xmlBundle )
{
  for my $x (@xmlBundle)
  {
    $bndl         = new Grid::GPT::V1::Bundle;
    $bndl->read_metadata_file($x);
    
    create_bundle();
    
    process_bundle();
  }
}
else
{
  $bndl           = new Grid::GPT::V1::Bundle;

  create_bundle();
  process_bundle();
}


sub process_bundle
{
  $bndl->clearBundleExcludedPackageList();
  $bndl->clearBundleIncludedPackageList();
  
  if( defined $srcdir )
  {
    #### We're obviously looking at making a source bundle:
    build_src();
  }
  else
  {
    ##### we've got a binary bundle
    build_bin();
  }
  write_bundle();
}



sub build_src
{
  $bundlefilename = bundlename('src');
  $pkgdir         = $locations->{'bindir'};
  my $srcdir         = $locations->{'srcdir'};

  $locations->create_dirs(mode => 'bundle');

  $dist           = new Grid::GPT::PkgDist( pkgdir => $srcdir, 
                                            all    => 1,
                                            log    => $log );


  my $pkg_pairs     = get_ordered_packages( dist => $dist, 
                                            pkgs => \@pkg_queries, 
                                            src  => 1 
                                          );


  if (flavored_bundle() eq 'yes') {
    my $core = $dist->query(pkgname => 'globus_core');

    if (! @$core) 
      {
        my $corename = get_core_src($pkgdir);
        $dist->load_gpt("$pkgdir/$corename");
        $core = $dist->query(pkgname => 'globus_core');
      }

    #### Add globus_core for GPT 1.x compatibility

    push @$pkg_pairs, { pkg => $core };
  }

  for my $p (@$pkg_pairs)
  {
    my $tarpath =  $p->{'pkg'}->gptpkgfile(full=>1);
    my $tarname = $p->{'pkg'}->gptpkgfile();

    push @sortedpkgs, $tarname;
    push @sortedpkgslist, $p->{'pkg'}->pkgname();

    $bndl->addIncludedPackage( pkgName => $p->{'pkg'}->pkgname(),
                               pkgVer  => $p->{'pkg'}->version_label(),
                               pkgFlav => $p->{'query'}->{'flavor'}, 
                               pkgType => $p->{'query'}->{'pkgtype'} 
                             );

#    print("cp -f  $tarpath $pkgdir/$tarname\n");
    my $result = system("cp -f  $tarpath $pkgdir/$tarname");
  }
  $bndl->{'TypeOfBundle'} = 'src';
  $packagelist            = "packaging_list";
} #### end source

sub build_bin
{
  $bundlefilename = bundlename('bin');

##  $pkgdir      = defined $tmpdir ? $tmpdir : $locations->{'bindir'};
  $pkgdir      = $locations->{'bindir'};

  $locations->create_dirs(mode => 'bundle');

  cleandir( $pkgdir ) if ! defined $keep;

  $installation = new Grid::GPT::Installation( locations      => $locations,
                                               with_filelists => 1,
                                               log            => $log
                                             );

  my $pkg_pairs = get_ordered_packages( dist    => $installation, 
                                        pkgs    => \@pkg_queries 
                                      );
  my @pkgs = map { $_->{'pkg'} } @$pkg_pairs;

  my $packager;
  $packager = create_bin_packages( pkgs => \@pkgs ) if ! defined $bundledef;

  for my $p (@pkgs)
  {
    push @sortedpkgs, $packager->{'gptfiles'}->{$p->label()} 
      if ! defined $bundledef;

    $bndl->addIncludedPackage( pkgName => $p->pkgname(),
                               pkgVer  => $p->version_label(),
                               pkgFlav => $p->flavor(), 
                               pkgType => $p->pkgtype()
                             );

    if( defined $native and  ! defined $bundledef ) 
    {
      push @nativesortedpkgs,$packager->{'rpmfiles'}->{$p->label()};
    }
  }

  $bndl->{'TypeOfBundle'} = 'gpt';
  $packagelist            = "packagelist";
} #### end binary


sub write_bundle
{
  if( defined($bundledef) )
  {
    my $file = "$bundledef.gpt-bundle.xml";

    $bndl->output_metadata_file( $file );
    exit 0;
  }

  $bndlFile               = "$bndl->{'Name'}_bundle-" . 
                            $bndl->version_label() . 
                            ".gpt-bundle.xml";

  my $tar                 = Archive::Tar->new();
  my $realdir             = cwd();

  chdir($pkgdir);

  if (! ($bundlefilename =~ m/^\//))
  {
    $bundlefilename       = Grid::GPT::FilelistFunctions::abspath($bundlefilename);
  }

  my @nme =  split m!/!, $bundlefilename;
  my $i   = @nme;

  $bndl->setFileName( file_name => $nme[$i-1] );
  $bndl->output_metadata_file( $bndlFile );

  push @tst, $bndlFile;
  $tar->add_files(@tst);
  $tar->add_files(@sortedpkgs);

  if (defined $srcdir) 
  {
    $tar->add_data($packagelist, join "", map { "$_\n" }@sortedpkgslist);
  } 
  else 
  {
    $tar->add_data($packagelist, join "", map { "$_\n" }@sortedpkgs);
  } 

  if ($tar->write($bundlefilename,1))
  {
    print "Bundle written as $bundlefilename\n";
  }
  else
  {
    print "ERROR writing $bundlefilename.  Make sure any directories specified
    exist\n";
  }

  chdir($realdir);

  if (defined $native) 
  {
    my $tar               = Archive::Tar->new();
    my $realdir           = cwd();

    $bundlefilename = bundlename('rpm');
    $bndl->{'TypeOfBundle'} = 'rpm';
    my @nme =  split m!/!, $bundlefilename;
    my $i = @nme;
    $bndl->setFileName( file_name => $nme[$i-1] );
    $bndl->output_metadata_file( $bndlFile );

    chdir($pkgdir);

    $tar->add_files(@nativesortedpkgs, $bndlFile);
    $tar->add_data($packagelist, join "\n", @nativesortedpkgs);

    if ($tar->write($bundlefilename,1))
    {
      print "Bundle written as $bundlefilename\n";
    }
    else
    {
      print "ERROR writing $bundlefilename.  Make sure any directories specified exist\n";
    }

    chdir($realdir);
  }
}

sub create_bundle
{
  # Add overrides to bundle definition

  $bndl->setFlag( flag => "NoDeps" )   if defined $nodeps;
  $bndl->setFlag( flag => "NoSetups" ) if defined $nosetups;
  $bndl->{'Name'}       = $bundlename  if(defined($bundlename));

  ##$bndl->setBundleVersion( label => $bundleversion) 
  $bndl->setBundleVersion( version => $bundleversion) 
    if defined $bundleversion;

  $bndl->{'bundleStab'}    = $bundlestab    if(defined($bundlestab));
  $bndl->{'versionLabel'}  = $bundlelabel   if(defined($bundlelabel));

  if (@exclude_queries) 
  {
    for $e (@exclude_queries) 
    {
      $bndl->addExcludedPackage( pkgName => $e->{'pkgname'},
                                 pkgType => $e->{'pkgtype'},
                                 pkgVer  => "0.0",
                                 pkgFlav => $e->{'flavor'} 
                               );
    }
  }

  if (@pkg_queries) 
  {
    for $p (@pkg_queries) 
    {
      $bndl->addIncludedPackage( pkgName => $p->{'pkgname'},
                                 pkgType => $p->{'pkgtype'},
                                 pkgVer  => "0.0",
                                 pkgFlav => $p->{'flavor'} 
                               );
    }
  }

  # extract combined bundle definition + overrides

  my @flags =  $bndl->getFlags();

  $nodeps++ if grep { defined $_ and $_ eq 'NoDeps' } @flags;
  $nosetups++ if grep { defined $_ and $_ eq 'NoSetups' } @flags;

  @exclude_queries = ();

  for my $e (@{$bndl->getFullBundleExcludedPackageList()}) 
  {
    push @exclude_queries, { pkgname => $e->{'Name'},
                             flavor  => $e->{'Flavor'},
                             pkgtype => $e->{'Type'}
                           };
  }

  @pkg_queries = ();

  for my $p (@{$bndl->getFullBundleIncludedPackageList()}) 
  {
    push @pkg_queries, { pkgname => $p->{'Name'},
                         flavor  => $p->{'Flavor'},
                         pkgtype => $p->{'Type'}
                       };
  }

##  $bndl->clearBundleExcludedPackageList();
##  $bndl->clearBundleIncludedPackageList();
}

sub create_template
{
  $bndl->{'Name'} = defined $bndl->{'Name'} 
                      ? $bndl->{'Name'} 
                      : "NEED A NAME";

  if (!@exclude_queries) 
  {
    $bndl->addExcludedPackage( pkgName => "PACKAGE NAME",
                               pkgVer  => "0.0",
                               pkgFlav => "PACKAGE FLAV" );
  }
  
  if (!@pkg_queries) 
  {
    $bndl->addIncludedPackage( pkgName => "PACKAGE NAME",
                               pkgVer  => "0.0",
                               pkgFlav => "PACKAGE FLAV" );
  }

  $bndl->addComponentName( "NEED A NAME" );
  $bndl->addComponentVer(  "NEED A VERSION" );
  $bndl->addComponentDesc( "NEED A DESCRIPTION" );

  $bndl->output_metadata_file( "BundleTemplateXML_FILE.xml" );
}


sub create_bin_packages 
{
  my(%args)    = @_;
  my $packager = 
    new Grid::GPT::PkgMngmt::Archive(locations => $locations,
                                     gpt_rpm_prefix => $rpmprefix,
                                     buildno => $buildno,
                                     license => $rpmlicense,
                                     rpm => $native,
#                                     verbose=> $verbose,
                                     log=> $log,
                                     skip => $keep);

  my $pkgs= $args{'pkgs'};

my $startdir = getcwd();

  chdir $pkgdir;
  $packager->archive($pkgs);
  chdir $startdir;

  return $packager;
}


sub get_ordered_packages 
{
  my(%args)               = @_;

  my ( $dist, 
       $pkgnames,
       $src
     ) = ( $args{'dist'},
           $args{'pkgs'},
           $args{'src'}
         );

  my $pkgset              = new Grid::GPT::PkgSet(log => $log);
  my %nodeptypes; # hash which tracks package types for the nodeps
  my %pkghash; # hash which tracks packages and their deps

  for my $e (@exclude_queries) 
  {
    $dist->add_exclusion( query => $e );
  }

  $dist->exclude_setups() if defined $nosetups;

  if (defined $all)
  {
    my $pkgArray          = $dist->pkgs();
    $nodeps++;

    # remove duplicates
    for my $p (@$pkgArray)
    {
      my $plabel          = $p->label();

next if $plabel =~ m!globus_core!;

      if ( !defined($pkghash{$plabel}) )
      {
        $pkghash{$plabel} = { query => { pkgname => 'ANY',
                                         flavor  => 'ANY',
                                         pkgtype => 'ANY' },
                              pkg   => $p};
        $pkg_count += 1;
      }
    }
  }

  my $error_messages;
  my $message;
  my $result;
  my @bad;

  for my $q (@$pkgnames) 
  {
    my $preferred_flavor = $q->{'flavor'} if $q->{'flavor'} ne 'ANY';
    my $pkgs             = matchPackage( query => $q, 
                                         dist  => $dist, 
                                         src   => $src );

    push @bad, $q if ! @$pkgs;

    for my $p (@$pkgs) 
    {
      my $msg = "Matching Query pkgname=>$q->{'pkgname'} flavor=>$q->{'flavor'} pkgtype=>$q->{'pkgtype'} Found:\n";

      next if ( $dist->should_exclude( pkg => $p ) );

      if ( ! defined($pkghash{$p->label()}) )
      {
        $msg .= $p->label() . "\n";
        $pkghash{$p->label()} = {query => $q, pkg => $p};
        $pkg_count += 1;
        $log->inform("Adding: " . $p->label()); 
      }

      $log->debug($msg);
    }
  }

  if (! defined $nodeps) 
  {
    my @added_pkgs = map { $_->{'pkg'} } values %pkghash;

    for my $ap (@added_pkgs) 
    {
      # get list of depenvs depending on pkgtype.
      # If the sdk flag is set then use that list instead.
      my @depenvs = @{$pkgtype2depenv{defined $sdk ? 'sdk' 
                                                   : $ap->pkgtype()}};

      $dist->cleardepenv();

      for my $d (@depenvs)
      {
        next if $d eq 'Setup' and defined $nosetups;
        $dist->set_depenv( $d );
      }

      my $depset  = $dist->extract_pkgset( pkgs             => [$ap], 
                                           preferred_flavor => $preferred_flavor
                                         );

      $message    = "";
      $result     = $depset->check_missing( log => \$message );

      if ($result)
      {
        $error_messages .= $message;
      }

      # get dependency tree
      my $deppkgs = $depset->pkgs();

      # remove duplicates
      for my $p (@$deppkgs)
      {
        my $plabel = $p->label();

        if ( !defined($pkghash{$plabel}) )
        {
          # Include only one flavor of a program package.

          if ($p->pkgtype() =~ m!pgm!) 
          {
              my @other_flavors = grep {
                   $_->{'pkg'}->pkgname() eq $p->pkgname() and 
                   $_->{'pkg'}->pkgtype() =~ m!pgm!
                 } values %pkghash;

              next if @other_flavors;
          }

          $pkg_count += 1;
          $pkghash{$plabel} = {query => undef, pkg => $p};
          $log->inform( "Adding Dep Pkg: " . $p->label() ); 
        }
      }
    }
  }

  if (@bad)
  {
    $error_messages .= 
      "ERROR: The following queries did not match any packages:\n";

    for my $q (@bad) 
    {
      $error_messages .= 
        "\tpkgname=>$q->{'pkgname'} flavor=>$q->{'flavor'} pkgtype=>$q->{'pkgtype'}\n";
    }
      
  }

  if (defined $error_messages and length($error_messages))
  {
    print STDERR $error_messages;
    die();
  }

  if ($pkg_count < 1 or (defined $srcdir and $pkg_count < 2))
  {
    Pod::Usage::pod2usage( -verbose => 0, 
                           -exitval => 1,
                           -output  => \*STDERR,
                           -message => 
                             "ERROR: no packages found to put in the bundle!"); 
  }

  map { $pkgset->add_package(pkgnode=> $_) } 
    map { $_->{'pkg'} } values %pkghash;

  if (defined $sdk or defined $src) 
  {
    $pkgset->cleardepenv();
    $pkgset->set_depenv('Build');
    $pkgset->sort_pkgs();
  } 
  else 
  {
    $pkgset->simple_sort_pkgs();
  }

  # Globus_Core games for GPT-1.x compatibility.

  my @nocorepkgs = grep { $_->pkgname ne 'globus_core' } 
      reverse @{ ( $pkgset->sorted())};
  my @corepkgs = grep { $_->pkgname eq 'globus_core' } 
      @{ ( $pkgset->sorted() )};

  my @bundledefs;

  for my $p (@nocorepkgs, @corepkgs) 
  {
    my @pairs = grep { $p->is_same($_->{'pkg'})} values %pkghash;

    if (! @pairs) 
    {
      print STDERR "ERROR: Internal error cannot match  ", $p->label(), "\n";

      for my $pa (values %pkghash) 
      {
        my($pkgname, $flavor, $pkgtype, $label) = 
          ($pa->{'query'}->{'pkgname'}, 
           $pa->{'query'}->{'flavor'}, 
           $pa->{'query'}->{'pkgtype'}, 
           $pa->{'pkg'}->label());

        print STDERR "/pkgname=$pkgname/flavor=$flavor/pkgtype=$pkgtype->$label\n";
      }

      die;
    }

    push @bundledefs, $pairs[0];
  }

  @bundledefs = reverse @bundledefs;

  return \@bundledefs;
}


sub matchPackage
{
  my(%args)  = @_;
  my ($pkgname, $flavor, $pkgtype, $src) = ( $args{'query'}->{'pkgname'}, 
                                             $args{'query'}->{'flavor'}, 
                                             $args{'query'}->{'pkgtype'},
                                             $args{'src'}
                                           );
  my $dist = $args{'dist'};
  my $pkgs;

  $pkgtype = 'src' if defined $src;
  #          $pkgtype = $pkgtype eq 'pgm_static' ? 'pgm' : $pkgtype;

  $log->debug("Query with: \
/pkgname=$pkgname/flavor=$flavor/pkgtype=$pkgtype\n");
 
  $pkgs = $dist->query( pkgname => $pkgname,
                        flavor  => $flavor,
                        pkgtype => $pkgtype );
  
  if(!@$pkgs and $pkgtype eq 'pgm')
  {
    # check for pgm_static packages
    $log->debug("pgm_static Query with: \
/pkgname=$pkgname/flavor=$flavor/pkgtype=pgm_static\n");

    $pkgs = $dist->query( pkgname => $pkgname,
                          flavor  => $flavor,
                          pkgtype => 'pgm_static' );
  }

  if(!@$pkgs and $pkgname =~ m!setup!)
  {
    # check for any flavor setup packages

    $log->debug("Flavor Setup Query with: \
/pkgname=$pkgname/flavor=ANY/pkgtype=$pkgtype\n");

    $pkgs = $dist->query( pkgname => $pkgname,
                          pkgtype => $pkgtype );
      
    if(!@$pkgs)
    {
      # check for pgm  setup packages
      $newtype = $pkgtype eq 'pgm' ? 'pgm_static' : 'pgm';

      $log->debug("Flavor Setup pgm Query with: \
/pkgname=$pkgname/flavor=ANY/pkgtype=$newtype\n");

      $pkgs = $dist->query( pkgname => $pkgname,
                            pkgtype => $newtype );
    }
  }

  my $msg = "Query Results: \n";
    
  for my $p (@$pkgs) 
  {
    $msg .= "\t" . $p->label() . "\n";
  }

  $log->debug($msg); 

##  @$pkgs = grep { ! $dist->should_exclude(pkg => $_)} @$pkgs;

  return $pkgs;
}

sub bundlename 
{
  my ($format) = @_;

  if (defined $outputfile) 
  {
    return $outputfile if $format ne 'rpm';
    my $nativefile = $outputfile;
    $nativefile =~ s!\.tar\.gz!_$format\.tar\.gz!;
    return $nativefile;
  }

  if (! defined $arch) 
  {
  # Add path to LocalEnv module to @INC
  push @INC,"$gpath/var/lib/perl";

  die "ERROR: GPT is not configured. Use gpt-config\n" 
    if ! defined eval ("require Grid::GPT::LocalEnv;");

  require Grid::GPT::LocalEnv;

  $arch = Grid::GPT::LocalEnv::get_target();
  }

  my $version = $bndl->version_label();

  my $file = "$bndl->{'Name'}-$version-" . 
             ($format ne 'src' ? "$arch-" : "")  . "$format" . 
             ($format eq 'src' ? "_bundle" : "") . ".tar.gz";

  return Grid::GPT::FilelistFunctions::abspath($file); 
}

sub cleandir
{
  my ($top)    = @_;

  opendir(DIR, $top);
  my @contents = readdir DIR;
  closedir DIR;

  my @dirs     = grep { -d "$top/$_" and ! m!^\.! } @contents;
  my @files    = grep { ! -d $_ } map { "$top/$_" } @contents;

  for my $f (@files) 
  {
    unlink($f) or warn "couldn't unlink $f: $!";
  }

  for my $d (@dirs) 
  {
    cleandir("$top/$d");
  }
}


# This less then glorious hack needs to be done in modules
sub get_core_src {

  my $dir = shift;
  require Grid::GPT::PkgMngmt::SetupBuildFlavors;
  my $core_src    = Grid::GPT::PkgMngmt::SetupBuildFlavors::get_core_src();

  my $pkg=Grid::GPT::PkgDist::get_pkgdata_from_tar($core_src);

  die "ERROR: Cannot create a source bundle without a globus_core package" 
    if ! defined $pkg;

  my $corename = "globus_core-" . $pkg->{'Version'}->label() . ".tar.gz";
  my $result      = system("cp -f $core_src $dir/$corename");

  return $corename;
}

sub flavored_bundle {
  my ($bundle) = @_;
  for my $p (map {$_->{'pkg'}} @$bundle) {
    next if ! defined $p->{'depnode'}->{'With_Flavors'};
    return 'yes' if $p->{'depnode'}->{'With_Flavors'} eq 'yes';
  }
  return 'no';
}

__END__

=head1 NAME

B<gpt-bundle> - Creates binary or source bundles from an installation or a collection of source packages.

=head1 SYNOPSIS

B<gpt-bundle> [options] packages 

  Options:

     -verbose                       Print copious output
     -help                          Print usage
     -man                           Print man page.
     -version                       Print GPT version.
     -srcdir=PATH                   Directory containing source packages
     -tmpdir=PATH                   Directory used to create binary packages
     -installdir=PATH               Directory containing an installation
     -bn=NAME                       Name of the bundle
     -bv=MAJOR.MINOR                Version of the bundle
     -bs=STABILITY                  Stability of the bundle contents
     -bl=VERSION                    Version Label of the bundle
     -native                        Create bundles of native pkgs (RPMs only).
     -all                           Bundle everything in the package directory..
     -nodeps                        Don't include any dependent packages.
     -nosetups                      Don't include dependent setup packages.
     -template                      Outputs empty Bundle Def XML file. 
     -bundledef=FILE                Outputs XML file with command line values. 
     -exclude=PACKAGE               Don't include PACKAGE in bundle
     -config=FILE                   Use bundle options stored in FILE
     -xml=FILE			    XML bundle description file
     -output=FILE                   Name of the bundle file,
     -rpmprefix=PATH                Absolute path encoded in the RPM.
     -buildnumber=NUMBER            Build number used for pgm_static packages
     -rpmlicense=LABEL              License Label added to the RPM header
     [packages]                     List of packages to be bundled

=head1 DESCRIPTION

B<gpt-bundle> Creates binary or source bundles from an installation or a
collection of source packages.  These bundles can then distributed be
installed using I<gpt-install> or I<gpt-build>. A bundle consists of a
collection of packages and a XML formatted description file. The DTD
for this file is found in I<$GPT_LOCATION/etc/gpt_bundle.dtd>.

The script accepts a list of packages from the command line or from an
inputed bundle description file.  It expands this list to include all
of the packages that the listed packages depend on. It then uses this
expanded list to create a new bundle description file which is then
added to the collection of packages to form a bundle.

B<gpt-bundle> assumes that all of the packages needed to create a
source bundle are found in one subdirectory identified by the
I<-srcdir> switch.

B<gpt-bundle> assumes that all of the packages need to create a binary
bundle are installed in a location identified by the I<-installdir>
flag or $GLOBUS_LOCATION.

The list of packages that can be entered from the command line are of
the form I<NAME-FLAVOR-PACKAGE_TYPE>.  The wildcard character '*' can
also be used.

=head1 OPTIONS

=over 8

=item B<-srcdir=PATH>

Specifies where the directory containing your source packages is.
If this is used, a source bundle will be created.  Note that this flag
is required to create source bundles.

=item B<-installdir=PATH> 

Specifies where the installation used for creating binary bundles is.
$GLOBUS_LOCATION is the default.

=item B<-xml=FILE> 

Specifies the input bundle description file.  A new file will be
generated using the contents of this file and included in the bundle.

=item B<-bundlename=NAME>

The name of the bundle.  This name is stored in the bundle description
file and used as part of the bundle filename if B<-output> is not
specified.


=item B<-bundleversion=MAJOR.MINOR>

This is the bundle version number.  It is stored in the bundle
description file and used for bundle updates.  It is also used for the
bundle filename if B<-bundlelabel> is not specified.

=item B<-bundlelabel=VERSION>

This is the bundle version label. It is stored in the bundle
description file as a string and so can accomodate any versioning
scheme.  The label is used in user queries, and as part of the bundle
filename.

=item B<-bundlestability=STABILITY>

This is the bundle version stability.  It is a field that is used to
indicate the stability of this version of the bundle.  The field can
have values of B<experimental>, B<alpha>, B<beta>, or B<production>.  The
default vaule is experimental.  The field is stored in the bundle description
file and used for user queries.

=item B<-tmpdir=PATH> 

Specifies the where the directory is that B<gpt-bundle> should use to
store generated binary packages.


=item B<-all>

Tells B<gpt-bundle> to bundle all of the packages in the source
package directory or installation.

=item B<-native>

Tells B<gpt-bundle> to also bundle packages that are in the native
format of the operating system.  Presently this only works for RPMS on
linux.  B<gpt-bundle> will output two bundle giving the native bundle
a platform specific extension such as -rpm.

=item B<-nodeps>

Tells B<gpt-bundle> to only include the packages specified--do not check for or
include dependencies.

=item B<-nosetups>

Tells B<gpt-bundle> to exclude any packages that are pulled in by a
setup dependencies.  Note that setup packages that are listed on the
command line will still be added to the bundle..

=item B<-exclude=package1,package2,...>

This option excludes the listed packages from the created bundle.  It
can be specified multiple times with the final listing used being an
aggregate of all the listed packages.  This option can be abbreviated
as '-exclude=package1,package2,...'.

=item B<-output=FILE>

This is an alternative way to specify the bundle name.  The output
file is put in the directory from which B<gpt-bundle> was invoked if
no path was given, in a directory relative it if a relative path was
given, or in the absolute location, if given.

=item B<-template>

Creates an empty Bundle XML that can be modified and used to create
a new bundle.

=item B<-bundledef=FILE>

Creates an XML file with name equal to B<FILE>.gpt-bundle.xml.  This
file will contain a GPT Bundle definition with values set to those 
passed in on the command line.

=item B<-rpmprefix=<path_to_installation>>

This should be set to the path to your GLOBUS_LOCATION.  GPT will use a
value of "/usr/grid" if this option is not specified.

=item B<-rpmlicense=<label>>

Provides an alternate copyright label for the rpms. The default is
whatever GPT was configured with.

=item B<-help>

Print a brief help message and exits.

=item B<-buildnumber=<number>>

Build number used to version static packages.

=item B<-man>

Prints the manual page and exits.

=item B<-version>

Prints the version of GPT and exits.

=back

=head1 Bundle Definition File Overrides

The I<-bundle*> switches can be used to override content in the bundle
definition file inputted by the I<-xml> switch.  The relationship
between the switches and the bundle definition file contents is shown
in the following table:

  I<Switch>         I<Element>         I<Attribute>

  -bundlename       GPTBundleData      Name
  -bundleversion    BundleReleaseInfo  Major, Minor
  -bundlelabel      BundleReleaseInfo  VersionLabel
  -bundlestability  VersionStability   Release

=head1 Bundle Names and Versions

B<gpt-bundle> encourages a naming convention for bundle filenames.
The convention can be overriden using the I<-output> switch.  The
convention is as follows:

  NAME-VERSION-src_bundle.tar.gz for source bundles
  NAME-VERSION-ARCH-gpt.tar.gz for gpt binary bundles. ARCH is the platform ID.
  NAME-VERSION-ARCH-rpm.tar.gz for rpm binary bundles.

NAME comes from the I<-bundlename> switch.  VERSION comes from the
I<-bundlelabel> switch.  If this switch is not used then VERSION
comes from the I<-bundleversion> switch.

Unless overrided by the switches previously mentioned, the values for
can also be extracted from the bundle definition file inputted by the
I<-xml> switch.

=head1 SEE ALSO

gpt-build(1) gpt-install(1) gpt-pkg(1) 

=head1 AUTHOR

Michael Bletzinger E<lt>mbletzin@ncsa.uiuc.eduE<gt>,  Eric Blau E<lt>blau@mcs.anl.govE<gt> and Patrick Duda E<lt>pduda@ncsa.uiuc.eduE<gt>

=cut

