#!/usr/bin/perl -w

=head1 NAME

dh_netdeps - calculates .NET dependencies

=cut

use strict;
use File::Find;
use Debian::Debhelper::Dh_Lib;

#eval 'use Debian::Debhelper::Dh_Lib';
#print "You need to install the debhelper package in order to use this program!" if $@;

=head1 SYNOPSIS

B<dh_netdeps> [S<I<debhelper options>>]

=head1 DESCRIPTION

dh_netdeps is a debhelper program that is responsible for generating the
${net:Depends} substitutions and adding them to substvars files.

The program will look at .dll/.exe and .config files in your package, and
will use the embedded dependency information to generate a dependency
strings on assembly and shared libs packages, including the setting of
version ranges (as declared by the shlibs/netlibs files of the used
packages). The dependency on a certain Mono version will be also added
to the final variable.

Note: the dependencies on shared libraries may be not resolved correctly
if there are no .config files associated with the the .exe/.dll file
which refers to the particular shared library (by its SONAME).

If you use this program, your package should build-depend on mono-utils
(>> 0.95).

=head1 OPTIONS

=over 4

=item B<-d>

Attempt to predict and avoid duplicates that may appear if you package
both, native shared libraries and DLL assemblies in one package.
The list of possibly duplicating candidates is expected to be in the
variable shlib:Depends from debian/package.substvars.

=item B<-r>

Don't set a strong versioned dependy on mono-jit or mono-mint packages.
This option can be used to specify a relaxed dependency on the VM
by-hand in the control file, eg. "mono-jit | cli-virtual-machine".

=cut

init();

my $cli = '/usr/bin/cli';

# The current python major version
# my $python_major;
my $mono_version = `$cli -V 2>&1`;

if ( !-x "/usr/bin/monodis" ) {
    error(
"Package mono-utils is not installed, aborting. (Probably forgot to Build-Depend on mono-utils.)"
    );
}

if ( system "grep -q \"Build-Dep.*mono-utils.*0.9\" debian/control" ) {
    warning("Warning! No Build-Depends on mono-utils (>= 0.96)!");
}

if ( !defined $mono_version || $mono_version eq "" ) {
    error( "Mono is not installed, aborting. (Probably forgot to
  Build-Depend on cli-virtual-machine.)"
    );
}
elsif ( $mono_version =~ /(mint|version)\ ([\d\.]+)/ ) {
    $mono_version = "$2";
}
else {
    error("Unable to parse Mono version out of \"$mono_version\".");
}

# The next python version
#my $python_nextversion = $python_version + 0.1;
#my $python_nextmajor = $python_major + 1;
#
#my @python_allversions = ('1.5','2.1','2.2','2.3');
#foreach (@python_allversions) {
#	s/^/python/;
#}

# Check for -V
my $usemono = $mono_version;
if ( $dh{V_FLAG_SET} ) {
    $usemono = $dh{V_FLAG};
}

# Cleaning the paths given on the command line
foreach (@ARGV) {
    s#/$##;
    s#^/##;
}

my $fh;
my %libdata;
open( $fh, "cat /var/lib/dpkg/info/*.netlibs debian/*/DEBIAN/netlibs 2>/dev/null |" );
while (<$fh>) {
    /(\S+)\s+(\S+)\s+(\w.*)\n?/;
    $libdata{"$1/$2"} = $3;
}

my %shlibdata;

sub resolveShlib {
    our( $file, $name, $outRef ) = @_;
    if ( !%shlibdata ) {
        open( $fh,
"cat /var/lib/dpkg/info/*.shlibs debian/shlibs.local debian/*/DEBIAN/shlibs 2>/dev/null |"
        );
        while (<$fh>) {
            /(\S+)\s+(\S+)\s+(\w.*)\n?/;
            $shlibdata{"$1.so.$2"} = $3;
        }
    }
#    if ( -r "$file.config"
#        && `cat $file.config` =~
#        /dll=\W*$name[^>]+\Wtarget\W*=\W*(\w[\w.\-_\d]+)/
#        && defined( $shlibdata{$1} ) )
    if ( -r "$file.config"
        && `cat $file.config` =~
        /dll=\W*$name[^>]+\Wtarget\W*=\W*(\w[\w.\-_\d]+)/
        && defined( $shlibdata{$1} ) )
    {
        $$outRef = $shlibdata{$1};
        return 1;
    }
    return 0;
}

sub extraDeps {
   my $config=$_[0].".config";
   return undef if (! -r $config);
   my $ret=undef;

   if(!%shlibdata) {
      open( $fh, "cat /var/lib/dpkg/info/*.shlibs debian/shlibs.local debian/*/DEBIAN/shlibs 2>/dev/null |" );
      while (<$fh>) {
         /(\S+)\s+(\S+)\s+(\w.*)\n?/;
         $shlibdata{"$1.so.$2"} = $3;
      }
   }

   $config=`cat $config`;
   while($config=~s/\Wtarget\W*=\W*(\w[\w.\-\d]+)//) {
      $ret.= (", ".$shlibdata{$1}) if(defined($shlibdata{$1}));
   }
   $ret=~s/^, // if $ret;
   return $ret;
}
foreach my $package ( @{ $dh{DOPACKAGES} } ) {
    my $tmp = tmpdir($package);
    my %deps;
    my @depkgs;

    delsubstvar( $package, "net:Depends" );    # for idempotency

    # find binaries
    find sub {
        my $vers;
        return unless -f and /\.(exe|dll)$/;
        local *F;
        my $file = $_;
        return unless open F, "LANG=C monodis --assemblyref $file 2>&1 |";
        if ( my $extra = extraDeps ($file) ) {
           push ( @depkgs, $extra );
        }
        our( $vers, $name, $key );
        while (<F>) {
            $vers = $1 if /Version=(.*)\n/;
            $name = $1 if /Could not find assembly ([^,]+),/;
            $name = $1 if /Name=(.*)\n/;
            $vers = "$1.$2" if /Major\/Minor:\s*(\d+),(\d+)/;
            $vers .= ".$1.$2" if /Build:\s*(\d+),(\d+)/;

            if (/0x\S+:.([ABCDEF0123456789 ]+)\n/ || /Token:\s*(\w+)/) {
                $key = $1;
                $key =~ s/\ //g;
                $key = $vers . "__" . lc($key);
                my $compat = "$name/$key";
                if ( !defined( $libdata{$compat} ) ) {

                    # reuse the key variable
                    #if ( resolveShlib( $file, $name, \$key ) ) {
                    #    push ( @depkgs, $key );
                    #}
                    #else
                    {
                        warning(
"Warning! No Debian dependency data for $name ($key)!"
                        );
                    }
                }
                else {
                    push ( @depkgs, $libdata{$compat} );
                }

                #print "ok, ".$deps{ "$name/$vers" . "__" . lc($key) };
            }
        }
        close F;
     }, $tmp;
    my %depkgsFiltered;
    for (@depkgs) {
       for ( split ( /\s*,\s*/, $_ ) ) {
          # filter dupes and don't depend on this package
          /^(\S+)/;
          $depkgsFiltered{$_} = 1 if($1 ne $package);
       }
    }
    # now filter the dupes coming from shlibs
    if ( defined( $dh{D_FLAG} ) ) {
       if(open( $fh, "<debian/$package.substvars" )) {
          while (<$fh>) {
             if (/^shlibs:Depends=(.*)\n?/) {
                for ( split ( /\s*,\s*/, $1 ) ) {
                   delete $depkgsFiltered{$_};
                }
             }
          }
       }
       else
       {
          verbose_print(
             "Could not read debian/$package.substvars"
          );
       }
    }

    addsubstvar( $package, "net:Depends",
        ( (defined($dh{R_FLAG})) ? "" : "mono-jit (>= $mono_version) | mono-mint (>= $mono_version)" )
        . join ( ", ", "", sort {
           # beautify the sort order, requested by meebey
           my $apkg;
           $a=~/^\S+/;
           $apkg=$&;
           $b=~/^\S+/;
           if($apkg eq $&) {
              return -1 if( ($a=~/>=/) && ($b=~/<</));
              return 1 if( ($b=~/>=/) && ($a=~/<</));
           }
           $a cmp $b;
        } ( keys %depkgsFiltered ) ) );
}

=head1 SEE ALSO

L<debhelper(7)>

This program is a part of mono-utils.

=head1 AUTHOR

Eduard Bloch <blade@debian.org>, partialy based on code from Brendan O'Dea <bod@debian.org>.

=cut

