#!/usr/bin/perl
# ========================================================= #
#                                                           #
#   File      : build_fat_tarball.pl                        #
#   Purpose   : pack an executable with dynamic libs        #
#                                                           #
#   Coded by Ralf Westram (coder@reallysoft.de) in Jun 22   #
#   http://www.arb-home.de/                                 #
#                                                           #
# ========================================================= #

use strict;
use warnings;

sub die_usage($) {
  my ($msg) = @_;

  print STDERR "Usage: build_fat_tarball.pl <binary> <readme> <tarball>\n";
  print STDERR "Purpose: pack <binary> plus required dynamic libraries into <tarball>\n";

  die "$msg\n ";
}

sub skip_lib($) {
  my ($lib) = @_;
  # result: 0=include lib, 1=skip lib, 2=include, but inactive

  return 1 if $lib =~ /^\/lib\//o; # ignore libs in /lib
  return 2 if $lib =~ /libglib/o; # deactivate libglib
  return 0;
}

sub find_dynamic_libs($) {
  my ($binary) = @_;
  # result: array of dynamic libraries to include
  # if library name starts with '-' => want library included, but deactivated

  my @libs = ();
  my $cmd = 'ldd '.$binary;
  open(LDD, $cmd.'|') || die "failed to fork '$cmd' (Reason: $!)";
  my $line;
  my $reg = qr/\s*([^\s]*)\s*=>\s*([^\s]*)\s*\(.*\)/;
  while (defined($line=<LDD>)) {
    chomp($line);
    if ($line =~ /$reg/) {
      my ($libname,$libfull) = ($1,$2);

      if ($libfull =~ /^-/o) { die "library names may not start with '-' ($libfull)"; } # when this occurs: fix separator used here
      if (not -e $libfull) { die "dynamic library $libfull does not exist"; }

      my $skip = skip_lib($libfull);
      if ($skip==1) {
        print "- ignoring '$libfull'\n";
      }
      else {
        print "- include  '$libfull'";
        if ($skip==2) {
          print " (inactive in subdir)";
          $libfull = '-'.$libfull;
        }
        print "\n";
        push @libs, $libfull;
      }
    }
  }
  close(LDD) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)";
  return @libs;
}

sub split_path_name($) {
  my ($file) = @_;
  if ($file =~ /^(.*)\/([^\/]*)$/o) {
    return ($1, $2);
  }
  else {
    die "failed to detect path of '$file'";
  }
}

sub add_file_nopath($$) {
  my ($file, $mode) = @_;
  if (not -e $file) { die "no such file: $file (cannot add to tarball)"; }

  my ($path, $name) = split_path_name($file);

  if ($mode==2) {
    my ($path2, $name2) = split_path_name($path);
    ($path, $name) = ($path2, $name2.'/'.$name);
  }

  return '--directory='.$path.' '.$name;
}

sub main() {
  my $args = scalar(@ARGV);
  if ($args<3) { die_usage("Error: missing arguments"); }

  my $binary  = shift(@ARGV);
  my $readme  = shift(@ARGV);
  my $tarball = shift(@ARGV);

  if (not -x $binary) {
    if (-e $binary) { die "Not an executable: $binary"; }
    die "No such file: $binary";
  }

  print "Pack fat tarball for $binary:\n";

  my @dyna_libs = find_dynamic_libs($binary);
  if (scalar(@dyna_libs)<1) { die "$binary does not refer to any dynamic libs"; }

  my $cmd = "tar -zcf $tarball --owner=0 --group=0 --dereference ".add_file_nopath($binary, 1);
  $cmd .= ' '.add_file_nopath($readme, 1);
  foreach my $lib (@dyna_libs) {
    if ($lib =~ /^-/) {
      my $_LIB = $';
      $cmd .= ' '.add_file_nopath($_LIB, 2);
    }
    else {
      $cmd .= ' '.add_file_nopath($lib, 1);
    }
  }

  system($cmd)==0 || die "error executing '$cmd' (exitcode=$?)\n";

  print `ls -al -h $tarball`;
}

main();
