#!/usr/bin/perl
#
# $FreeBSD: head/Tools/make_index 340851 2014-01-23 19:55:14Z mat $
#
# INDEX builds visit each port once and write out each port's
# *-depends as a list of directories, using 'make describe'.  This
# script goes back in and maps the directories back to pkgnames,
# fixes up the *-depends list, and writes out the new INDEX file.

require 5.002;

# Helper function to map a directory to a pkgname.
sub by_path {
    my ($name, $port) = @_;

  # If a direct mapping exists, then use it.
    return $by_path{$name} if (defined $by_path{$name});

  # Make sure we have /usr/ports at the beginning.
    $name =~ s!^$pwd!/usr/ports!o;
    return $by_path{$name} if (defined $by_path{$name});

  # Collapse all the '..' sequences.
    my @f = split('/', $name), @p = ();
    foreach (@f) { (/\.\./) ? pop(@p) : push(@p, $_); }
    $name = join('/', @p);
    return $by_path{$name} if (defined $by_path{$name});

    print STDERR "make_index: $port: no entry for $name\n";
    return undef;
}

# This routine replaces what used to be the time-consuming
# recursive 'depends-list' and 'package-depends' targets.
sub recurse {
    my $pkg = shift(@_);
    return if $pkg->{checked};

  # extract-depends = extract-depends + recursive list of run-depends
  #     for each extract-depends
    my @deps = ();
    foreach $name (@{$pkg->{edep}}) {
        recurse($index{$name});
	push(@deps, @{$index{$name}->{rdep}});
    }
    $pkg->{edep} = uniqify(@{$pkg->{edep}}, @deps);

  # same as above except for patch-depends this time
    @deps = ();
    foreach $name (@{$pkg->{pdep}}) {
	recurse($index{$name});
	push(@deps, @{$index{$name}->{rdep}});
    }
    $pkg->{pdep} = uniqify(@{$pkg->{pdep}}, @deps);

  # same as above except for fetch-depends this time
    @deps = ();
    foreach $name (@{$pkg->{fdep}}) {
	recurse($index{$name});
	push(@deps, @{$index{$name}->{rdep}});
    }
    $pkg->{fdep} = uniqify(@{$pkg->{fdep}}, @deps);
    $pkg->{checked} = 1;

  # same as above except for build-depends this time
    @deps = ();
    foreach $name (@{$pkg->{bdep}}) {
	recurse($index{$name});
	push(@deps, @{$index{$name}->{rdep}});
    }
    $pkg->{bdep} = uniqify(@{$pkg->{bdep}}, @deps);
    $pkg->{checked} = 1;

  # same as above except for run-depends this time
    @deps = ();
    foreach $name (@{$pkg->{rdep}}) {
	recurse($index{$name});
	push(@deps, @{$index{$name}->{rdep}});
    }
    $pkg->{rdep} = uniqify(@{$pkg->{rdep}}, @deps);
    $pkg->{checked} = 1;

}

# Given one or more lists as arguments return the set
# of unique elements among them.
sub uniqify {
    my %seen = ();
    my @unique = grep {! $seen{$_}++} (@_);
    return \@unique;
}

# Save where we are so that we can map all directories formed
# from ${PORTSDIR} to their canonical location '/usr/ports/...'.
chomp($pwd = `pwd`);

# Read each line of output generated by the 'index' target.
while (<>) {
    chomp;
    s/\015$//;

    my @f = split(/\|/);

  # Force to canonical form.
    $f[1] =~ s!^$pwd!/usr/ports!o;
    $f[4] =~ s!^$pwd!/usr/ports!o;

  # Save directory -> pkgname relationship.
  # Note: $f[0] gets clobbered by the splice below so we'll save
  # it to a new $name first.
    $by_path{$f[1]} = $name = $f[0];

  # Create a hash table of the infomation we need about this port.
    my $pkg = {
	'edep'		=> [split(/ /, $f[7])],
	'pdep'		=> [split(/ /, $f[8])],
	'fdep'		=> [split(/ /, $f[9])],
	'bdep'		=> [split(/ /, $f[10])],
	'rdep'		=> [split(/ /, $f[11])],
	'rest'		=> join('|', splice(@f, 12)),
	'text'		=> join('|', splice(@f, 0, 7))
    };
    $index{$name} = $pkg;

  # This is a cheap way of preserving the order of the entries.
    push(@names, $name);
}

# For each port perform the mapping between directory and pkgnames.
foreach $name (keys %index) {
    my $pkg = $index{$name};
  # first the extract dependencies
    if (@{$pkg->{edep}}) {
	my @edep = map { by_path($_, $name) } @{$pkg->{edep}};
	$pkg->{edep} = \@edep;
    }
  # then the patch dependencies
    if (@{$pkg->{pdep}}) {
	my @pdep = map { by_path($_, $name) } @{$pkg->{pdep}};
	$pkg->{pdep} = \@pdep;
    }
  # then the fetch dependencies
    if (@{$pkg->{fdep}}) {
	my @fdep = map { by_path($_, $name) } @{$pkg->{fdep}};
	$pkg->{fdep} = \@fdep;
    }
  # then the build dependencies
    if (@{$pkg->{bdep}}) {
	my @bdep = map { by_path($_, $name) } @{$pkg->{bdep}};
	$pkg->{bdep} = \@bdep;
    }
  # then the run dependencies
    if (@{$pkg->{rdep}}) {
	my @rdep = map { by_path($_, $name) } @{$pkg->{rdep}};
	$pkg->{rdep} = \@rdep;
    }
}

# With all that done we're finally ready to write out the new
# INDEX file one port at a time.
foreach $name (@names) {
    my $pkg = $index{$name};
    if (exists $pkg->{'PRINTED'}) {
        print STDERR "Warning: Duplicate INDEX entry: $name\n";
    } else {
	recurse($pkg);
	print "$pkg->{text}|";
	print join(' ', sort(@{$pkg->{bdep}})) if @{$pkg->{bdep}};
	print "|";
	print join(' ', sort(@{$pkg->{rdep}})) if @{$pkg->{rdep}};
	print "|$pkg->{rest}|";
	print join(' ', sort(@{$pkg->{edep}})) if @{$pkg->{edep}};
	print "|";
	print join(' ', sort(@{$pkg->{pdep}})) if @{$pkg->{pdep}};
	print "|";
	print join(' ', sort(@{$pkg->{fdep}})) if @{$pkg->{fdep}};
	print "\n";
	++$pkg->{'PRINTED'};
    }
}
