The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Perl::Dist::Strawberry::Step::CreateRelocationFile;

use 5.012;
use warnings;
use base 'Perl::Dist::Strawberry::Step';

use File::Find::Rule;
use File::Spec::Functions qw(catfile catdir);
use File::Slurp           qw(read_file write_file);

#XXX-TODO implementation is ugly (but works for now)

sub check {
  my $self = shift;
  my $rv = 1;
  
  for (qw/reloc1_out reloc2_out reloc1_in reloc2_in/) {
    warn "BEWARE: '$_' is DEPRECATED" if defined $self->{config}->{$_};
  }
  for (qw/reloc_out reloc_in/) {
    warn "BEWARE: missing '$_'" unless defined $self->{config}->{$_};
    $rv = 0;
  }
  
  return $rv;
} 

sub run {
  my $self = shift;
  if ($self->{config}->{reloc1_in} && $self->{config}->{reloc1_out}) {
    my $in1  = $self->boss->resolve_name($self->{config}->{reloc1_in});
    my $out1 = $self->boss->resolve_name($self->{config}->{reloc1_out});
    $self->_make_relocation_file1($in1, $out1); # .packlist + *.bat
  }
  if ($self->{config}->{reloc2_in} && $self->{config}->{reloc2_out}) {
    my $in2  = $self->boss->resolve_name($self->{config}->{reloc2_in});
    my $out2 = $self->boss->resolve_name($self->{config}->{reloc2_out});
    $self->_make_relocation_file2($in2, $out2); # win32/*.url
  }
  if ($self->{config}->{reloc_in} && $self->{config}->{reloc_out}) {
    my $in  = $self->boss->resolve_name($self->{config}->{reloc_in});
    my $out = $self->boss->resolve_name($self->{config}->{reloc_out});
    $self->_make_relocation($in, $out); # all in one go
  }
}

sub _make_relocation {
  my ($self, $file_in, $file_out) = @_;
  
  $self->boss->message(2, "gonna make reloc '$file_out'");
  
  # Find all the .packlist files.
  my @packlists_list = File::Find::Rule->file()->name('.packlist')->relative()->in( $self->global->{image_dir} );
  my %packlists = map { s{/}{\\}msg; $_ => 1 } @packlists_list;

  # Find all the .bat files.
  my @batch_files_list = File::Find::Rule->file()->name('*.bat')->relative()->in( $self->global->{image_dir} );
  my %batch_files = map { s{/}{\\}msg; $_ => 1 } @batch_files_list;
  
  # Find all the .url files in win32 subdir
  my @url_files_list = File::Find::Rule->file()->name('*.url')->relative()->in( catdir($self->global->{image_dir}, 'win32') );
  my %url_files = map { s{/}{\\}msg; "win32\\$_" => 1 } @url_files_list;

  # Print the first line of the relocation file.
  my $file_out_handle;
  open $file_out_handle, '>', $file_out or die "open fail";
  print {$file_out_handle} $self->global->{image_dir}, "\\\n";

  # Read the source file, writing out the files that actually exist.
  my @filelist = read_file($file_in);
  foreach my $filelist_entry (@filelist) {
    $filelist_entry =~ m/\A([^:]*):.*\z/msx;
    if ( defined $1 and -f catfile($self->global->{image_dir}, $1) ) {
      print {$file_out_handle} $filelist_entry;
    }
  }

  # Print out the rest of the .url files.
  foreach my $pl ( sort { $a cmp $b } keys %url_files ) {
    print {$file_out_handle} "$pl:backslash\n";
  }

  # Print out the rest of the .packlist files.
  foreach my $pl ( sort { $a cmp $b } keys %packlists ) {
    print {$file_out_handle} "$pl:backslash\n";
  }

  # Print out the batch files that need relocated.
  my $batch_contents;
  #XXX-FIXME this was original: my $match_string = q(eval [ ] 'exec [ ] ) . quotemeta catfile($self->global->{image_dir}, qw(perl bin perl.exe));
  my $match_string = quotemeta catfile($self->global->{image_dir}, qw(perl bin perl.exe));
  foreach my $batch_file ( sort { $a cmp $b } keys %batch_files ) {
    #$self->boss->message(3, "Checking to see if '$batch_file' needs relocated");
    $batch_contents = read_file( catfile($self->global->{image_dir}, $batch_file) );
    print {$file_out_handle} "$batch_file:backslash\n" if $batch_contents =~ m/$match_string/msgx;
  }

  # Finish up by closing the handle.
  close $file_out_handle or die "close failed";

  return 1;
}

#XXX-FIXME the rest is just for old MSI generation (will be removed in the next major release)

sub _make_relocation_file2 {
  my ($self, $file_in, $file_out) = @_;
  
  $self->boss->message(2, "gonna make reloc2 '$file_out'");
  
  # Find all the .url files in win32 subdir
  my @url_files_list = File::Find::Rule->file()->name('*.url')->relative()->in( catdir($self->global->{image_dir}, 'win32') );
  my %url_files = map { s{/}{\\}msg; "win32\\$_" => 1 } @url_files_list;
  
  # Print the first line of the relocation file.
  my $file_out_handle;
  open $file_out_handle, '>', $file_out or die "open fail";
  print {$file_out_handle} $self->global->{image_dir}, "\\\n";

  # Read the source file, writing out the files that actually exist.
  my @filelist = read_file($file_in);
  foreach my $filelist_entry (@filelist) {
    $filelist_entry =~ m/\A([^:]*):.*\z/msx;
    if (defined $1 and -f catfile($self->global->{image_dir}, $1) ) {
      print {$file_out_handle} $filelist_entry;
    }
  }
  
  # Print out the rest of the .url files.
  foreach my $pl ( sort { $a cmp $b } keys %url_files ) {
    print {$file_out_handle} "$pl:backslash\n";
  }
  
}
  
sub _make_relocation_file1 {
  my ($self, $file_in, $file_out, @files_already_processed) = @_;

  $self->boss->message(2, "gonna make reloc1 '$file_out'");

  # Find files we're already assigned for relocation.
  my @filelist;
  my %files_already_relocating;

  foreach my $file_already_processed (@files_already_processed) {
    @filelist = read_file( catfile($self->global->{image_dir}, $file_already_processed) );
    shift @filelist; # the first line is 'image_dir'
    %files_already_relocating = ( %files_already_relocating, map { m/\A([^:]*):.*\z/msx; $1 => 1 } @filelist );
  }

  # Find all the .packlist files.
  my @packlists_list = File::Find::Rule->file()->name('.packlist')->relative()->in( $self->global->{image_dir} );
  my %packlists = map { s{/}{\\}msg; $_ => 1 } @packlists_list;

  # Find all the .bat files.
  my @batch_files_list = File::Find::Rule->file()->name('*.bat')->relative()->in( $self->global->{image_dir} );
  my %batch_files = map { s{/}{\\}msg; $_ => 1 } @batch_files_list;

  # Get rid of the .packlist and *.bat files we're already relocating.
  delete @packlists{ keys %files_already_relocating };
  delete @batch_files{ keys %files_already_relocating };

  # Print the first line of the relocation file.
  my $file_out_handle;
  open $file_out_handle, '>', $file_out or die "open fail";
  print {$file_out_handle} $self->global->{image_dir}, "\\\n";

  # Read the source file, writing out the files that actually exist.
  @filelist = read_file($file_in);
  foreach my $filelist_entry (@filelist) {
    $filelist_entry =~ m/\A([^:]*):.*\z/msx;
    if ( defined $1 and -f catfile($self->global->{image_dir}, $1) ) {
      print {$file_out_handle} $filelist_entry;
    }
  }

  # Print out the rest of the .packlist files.
  foreach my $pl ( sort { $a cmp $b } keys %packlists ) {
    print {$file_out_handle} "$pl:backslash\n";
  }

  # Print out the batch files that need relocated.
  my $batch_contents;
  #XXX-FIXME this was original: my $match_string = q(eval [ ] 'exec [ ] ) . quotemeta catfile($self->global->{image_dir}, qw(perl bin perl.exe));
  my $match_string = quotemeta catfile($self->global->{image_dir}, qw(perl bin perl.exe));
  foreach my $batch_file ( sort { $a cmp $b } keys %batch_files ) {
    #$self->boss->message(3, "Checking to see if '$batch_file' needs relocated");
    $batch_contents = read_file( catfile($self->global->{image_dir}, $batch_file) );
    print {$file_out_handle} "$batch_file:backslash\n" if $batch_contents =~ m/$match_string/msgx;
  }

  # Finish up by closing the handle.
  close $file_out_handle or die "close failed";

  return 1;
}

1;