#!/usr/bin/perl -w
#
# Copyright (c) 1999 Neil Blakey-Milner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

# a script to gain a bit of knowledge about a supplied distfile

use strict;
use vars qw/ $opt_n /;

use File::Basename qw( basename dirname );
use Getopt::Std qw( getopts );

my ($filename, $ver, $initdir, $distdir, $distname, $wrkdir, $tmpdir);
my ($makefile, $wrksrc, $realprefix);
my (%cap);

$realprefix = "/usr/local";

chomp($initdir = `pwd`);

$cap{"MAINTAINER"} = ($ENV{"MAINTAINER"} || "ports\@freebsd.org");

getopts('n');

&usage if $#ARGV != 0;

$filename = $ARGV[0];

if ($filename =~ m#^(f|ht)tp\://#) {
	print `fetch $filename`;
	$filename = basename($filename);
}
chdir(dirname($filename));
chomp($distdir = `pwd`);
chdir ($initdir);

&chk_extract; # .tar.gz vs .tgz vs .tar.bz2, &c. and actually extract
&enh_distname;
do { print $cap{'PKGNAME'} if defined($cap{'PKGNAME'}); exit; } if $opt_n;
&chk_scripts; # configure, imake, &c.
&md5;
&runmake;

exit;

sub runmake {
	my($destdir);
	my($makeargs, $extra_args);
	my($user, $group);
	$destdir = $tmpdir . "/destdir/";
	chdir $tmpdir;
	open(LOG, ">log");
	select(LOG); $| = 1;
	select(STDOUT); $| = 1;

	$makeargs = "DISTDIR=$distdir ";
	chomp($user = `id -un`);
	chomp($group = `id -gn`);
	$makeargs .= "BINOWN=$user BINGRP=$group ";
	$makeargs .= "SHAREOWN=$user SHAREGRP=$group ";
	$makeargs .= "MANOWN=$user MANGRP=$group ";
	$makeargs .= "NO_PKG_REGISTER=1";

	if (defined($makefile)) {
		if (`grep -ai destdir $wrksrc/$makefile`) {
			$extra_args = "MAKE_ARGS='DESTDIR=$destdir' ";
		} else {
			$extra_args = "PREFIX=$destdir/usr/local ";
		}
	} else {
		$extra_args = "PREFIX=$destdir/usr/local ";
	}

	my($tryagain) = 1;
	while ($tryagain) {
		chdir $tmpdir;
		my (@configure);
		my (@build);
		$tryagain = 0;
		&writemk;
		`make clean`;

		@configure = `make configure $makeargs $extra_args 2>&1`;
		if ($?) {
			print @configure;
			die "$?";
		}
		print LOG @configure;
		print "configure finished successfully\n";

		if (!defined($makefile)) {
			if (-f "$wrksrc/Makefile") {
				delete($cap{"NO_BUILD"});
				delete($cap{"NO_INSTALL"});
				$makefile = "Makefile";
				`grep -ai "[^/]destdir" $wrksrc/$makefile`;
				#print "grep returned $?\n";
				if ($? != 256) {
					$extra_args = "MAKE_ARGS='DESTDIR=$destdir' ";
					print LOG @configure;
					print LOG "===mp===> Port supports destdir, retrying!\n";
				}
				$tryagain = 1;
				print LOG "===mp===> Port creates Makefile - reinstating build and install target...!\n";
				next;
			}
		}

		@build = `make build $makeargs $extra_args 2>&1`;
		if ($?) {
			if (!$cap{"USE_GMAKE"}) {
				if (grep { /^make: fatal errors encountered -- cannot continue/ } @build ) {
					$cap{"USE_GMAKE"} = "YES";
					$tryagain = 1;
					print LOG @configure;
					print LOG @build;
					print LOG "===mp===> Port failed without GMAKE, retrying!\n";
					next;
				}
				if (grep { /^make: don't know how to make all. Stop/ } @build ) {
					chdir($wrksrc);
					my($targets, $tmp, $tmp2);
					$targets = `make -dg1 -q | grep "^[^.#][-_a-zA-Z ]*" | awk "{\$1}" | tr '\\n' ' '`;
					$tmp = $cap{"PKGNAME"} || "";
					$tmp2 = $cap{"DISTNAME"};
					$tmp =~ s/-[^-]*$//g;
					$tmp2 =~ s/-[^-]*$//g;
					if ( (grep { /$tmp/ } $targets ) ||
						(grep { /$tmp2/ } $targets ) ||
						(grep { /build/ } $targets )) {
						$cap{"ALL_TARGET"} = $_;
						print LOG "===mp===> Port has alternate build target $_, retrying!.\n";
						$tryagain = 1;
						next;
					}
				}

			}
			print @configure;
			print @build;
			die "$?";
		}
		print LOG @build;
		print "build finished successfully\n";

	}
	my(@install);
	`mkdir -p $destdir/usr/local`;
	`/usr/sbin/mtree -U -f /etc/mtree/BSD.local.dist -d -e -p $destdir/usr/local`;
	`chdir $destdir/usr/local && find * | sort > $tmpdir/mtree.dirs`;
	mkdir ($tmpdir . "/pkg", 0777);
	`touch $tmpdir/pkg/PLIST`;
	`touch $tmpdir/pkg/DESCR`;
	`touch $tmpdir/pkg/COMMENT`;
	@install = `make install $makeargs $extra_args FALSE='/usr/bin/true' 2>&1`;
	print LOG @install;
	`chdir $destdir/usr/local && find * ! -type d | sort > $tmpdir/post-install.files`;
	`chdir $destdir/usr/local && find * -type d | sort > $tmpdir/post-install.dirs`;
	`cat $tmpdir/post-install.files > $tmpdir/pkg/PLIST`;
	`comm -23 $tmpdir/post-install.dirs $tmpdir/mtree.dirs | xargs -n1 echo \@dirrm >> $tmpdir/pkg/PLIST`;
}

sub writemk {

	my (@freeform);

	my (%tmp);
	my ($key);

	foreach $key (keys %cap) {
		$tmp{$key} = $cap{$key};
	}

	foreach $key (keys %tmp) {
		if ($tmp{"USE_KDE"}) {
			if ($tmp{"LIB_DEPENDS"}) {
			  $tmp{"LIB_DEPENDS"} .= "\t\\\n\tkdecore.2:\${PORTSDIR}/x11/kdelibs11";
			} else {
			  $tmp{"LIB_DEPENDS"} = "\tkdecore.2:\${PORTSDIR}/x11/kdelibs11";
			}
			delete($tmp{"USE_KDE"});
		}

		if ($tmp{"USE_OPENSSL"}) {
			if ($tmp{"USE_OPENSSL"} eq "MUST") {
	                        if ($tmp{"LIB_DEPENDS"}) {
					$tmp{"LIB_DEPENDS"} .=
						"\t\\\n\tcrypto.1:\${PORTSDIR}/security/openssl\t\\\n\tssl.1:\${PORTSDIR}/security/openssl";
                        	} else {
                          		$tmp{"LIB_DEPENDS"} =
						"\tcrypto.1:\${PORTSDIR}/security/openssl\t\\\n\tssl.1:\${PORTSDIR}/security/openssl";
                        	}
			} elsif ($tmp{"USE_OPENSSL"} eq "OPTIONAL") {
				push @freeform, ".if defined(USE_SSL)\n";
				push @freeform, "LIB_DEPENDS+=\tcrypto.1:\${PORTSDIR}/security/openssl\t\\\n\tssl.1:\${PORTSDIR}/security/openssl\n";
				push @freeform, ".endif\n";
			}
			$tmp{"CONFIGURE_ARGS"} .= "--with-openssl=$realprefix";
			delete($tmp{"USE_OPENSSL"});
		}

		if ($tmp{"USE_GNOME"}) {
			if ($tmp{"LIB_DEPENDS"}) {
				$tmp{"LIB_DEPENDS"} .= 
					"\t\\\n\tgnome.1:\${PORTSDIR}/x11/gnomelibs";
			} else {
				$tmp{"LIB_DEPENDS"} =
					"\tgnome.1:\${PORTSDIR}/x11/gnomelibs";
			}
			delete($tmp{"USE_GNOME"});
		}

		if ($tmp{"USE_GTK"}) {
			if ($tmp{"LIB_DEPENDS"}) {
				  $tmp{"LIB_DEPENDS"} .=
					"\t\\\n\tgtk12.1:\${PORTSDIR}/x11-toolkits/gtk12";
			} else {
				$tmp{"LIB_DEPENDS"} =
					"\tgtk12.1:\${PORTSDIR}/x11-toolkits/gtk12";
			}

			if ($tmp{"CONFIGURE_ENV"}) {
				$tmp{"CONFIGURE_ENV"} .=
					"\t\\\n\tGTK_CONFIG=\"\${X11BASE}/bin/gtk12-config\"";
			} else {
				$tmp{"CONFIGURE_ENV"} =
					"\tGTK_CONFIG=\"\${X11BASE}/bin/gtk12-config\"";
			}

			delete($tmp{"USE_GTK"});
		}
	}

	open(MAKEFILE, ">$tmpdir/Makefile");
	my ($date);
	chomp($date = `date +"\%d \%b \%Y"`);

	print MAKEFILE <<EOF;
# New ports collection makefile for: $distname
# Version required:	$ver
# Date created:		$date
# Whom:			makeport.pl
#
# \$FreeBSD\$
#

EOF

	# first group
	foreach $key (keys %tmp) {
		my($tmp) = $tmp{$key};
		if (($key eq "DISTNAME") || ($key eq "PKGNAME") || ($key eq "EXTRACT_SUFX")){
			print MAKEFILE "$key=\t$tmp\n";
			delete($tmp{$key});
		}
	}
	print MAKEFILE "CATEGORIES=	misc\n";

	print MAKEFILE "\nMAINTAINER=\t" . $tmp{"MAINTAINER"} . "\n\n";
	delete($tmp{"MAINTAINER"});

	if ($tmp{"LIB_DEPENDS"}) {
		print MAKEFILE "LIB_DEPENDS=". $tmp{"LIB_DEPENDS"} . "\n\n";
		delete($tmp{"LIB_DEPENDS"});
	}

	#second group
	foreach $key (keys %tmp) {
		my($tmp) = $tmp{$key};
		print MAKEFILE "$key=\t$tmp\n";
	}

	print MAKEFILE @freeform;

	print MAKEFILE "\n.include <bsd.port.mk>\n";	
	close (MAKEFILE);
}

sub md5 {
	my $tmp = basename($filename);
	mkdir ($tmpdir . "/files", 0777);
	chdir ($initdir);
	`md5 $filename | perl -pe "s#$filename#$tmp#" > $tmpdir/files/md5`;
}

sub chk_extract {
	my $tmp = basename($filename);

	$tmp =~ s/(\.tgz)$// && do {
		$cap{"DISTNAME"} = $tmp;
		$cap{"EXTRACT_SUFX"} = $1;
		&extract("tgz");
		return;
	};
	$tmp =~ s/(\.tar\.bz2)$// && do {
		$cap{"DISTNAME"} = $tmp;
		$cap{"EXTRACT_SUFX"} = $1 if ($1 ne ".tar.bz2");
		$cap{"USE_BZIP2"} = "YES";
		&extract("bzip");
		return;
	};
	$tmp =~ s/(\.tar)$// && do {
		$cap{"DISTNAME"} = $tmp;
		$cap{"EXTRACT_SUFX"} = $1;
		&extract("tar");
		return;
	};
	$tmp =~ s/(\.tar.*)$// && do {
		$cap{"DISTNAME"} = $tmp;
		$cap{"EXTRACT_SUFX"} = $1 if ($1 ne ".tar.gz");
		&extract("tgz");
		return;
	};
	warn "Don't understand $filename\'s suffix, assuming tgz";
	&extract("tgz");
}

sub extract {
	return if $opt_n;
	my ($method) = @_;
	$tmpdir = ($ENV{"TMPDIR"} || "/tmp") . "/" . $cap{'DISTNAME'} . ".$$/";
	$wrkdir = $tmpdir . "/work/";
	mkdir($tmpdir,0777);
	mkdir($wrkdir,0777);
	$method =~ /tgz/ && do {
		`tar xvzf $filename -C $wrkdir` || die $@;
		return;
	};
	$method =~ /tar/ && do {
		`tar xvf $filename -C $wrkdir` || die $@;
	  return;
	};
	$method =~ /bzip/ && do {
		`bzcat $filename | tar xvf - -C $wrkdir` || die $@;
	  return;
	};
}

sub add_extsuffix {
	return if $cap{"EXTRACT_SUFX"};
	if ($cap{"USE_BZIP2"}) {
		$cap{"EXTRACT_SUFX"} = ".tar.bz2";
	} else {
		$cap{"EXTRACT_SUFX"} = ".tar.gz";
	}
}

sub enh_distname {
	my($changes) = 0;
	my($play) = $cap{"DISTNAME"};
	while ($changes == 0) {
		$changes = 1;
		$_ = $play;
		/^(.*[0-9][a-z]?)([-._][a-z]*)$/i && do {
			$cap{"DISTNAME"} = $1;
			$play = $1;
			&add_extsuffix;
			$cap{"EXTRACT_SUFX"} = $2 . $cap{"EXTRACT_SUFX"};
			$changes = 0;
			next;
		};
		/^([a-z]*)([0-9]+)([-._])([0-9].*)$/i && do {
			my($tmp) = "$1-$2.$4";
			$cap{"PKGNAME"} = $tmp;
			$play = $tmp;
			$changes = 0;
			next;
		};
		/^(.*?)([-._])(v?)((?:[0-9][-._])*[0-9][a-z]?.*)$/i && do {
			my($tmp);
			$tmp = $1;
			$distname = $tmp;
			$ver = "-$4";
			if ($ver =~ /^(.*)([-._])([0-9]?)([a-z])[a-z]+([0-9]?)$/) {
				my($foo) = "";
				print "1 - $1\n";
				print "2 - $2\n";
				print "3 - $3\n";
				print "4 - $4\n";
				print "5 - $5\n";
				$foo = "." if $3;
				$ver = "$1$foo$3$4$5";
			}
			$tmp .= $ver;
			$cap{"PKGNAME"} = $tmp if ($tmp ne $cap{"DISTNAME"});
		};
		$ver = "" unless defined($ver);
		$distname = $cap{"PKGNAME"} || $cap{"DISTNAME"};
	}
}

sub chk_scripts {
	my (@dirs);
	chdir ($wrkdir);
	if (! -d $cap{"DISTNAME"}) {  # WRKSRC needs to be tweaked
		opendir(DIR, $wrkdir);
		@dirs = grep { ! /^.$/ && ! /^..$/ && -d "$wrkdir/$_" } readdir(DIR);
		closedir DIR;
		if ($#dirs < 0) { # NO_WRKSUBDIR
			$cap{"NO_WRKSUBDIR"} = "YES";
			$wrksrc = $wrkdir;
		} else { # set WRKSRC
			warn "More than one directory, using the first one" if ($#dirs != 0);
			$cap{"WRKSRC"} = '${WRKDIR}/' . $dirs[0];
			$wrksrc = $wrkdir . $dirs[0];
			chdir ($dirs[0]);
		}
	} else {
		$wrksrc = $wrkdir . $cap{"DISTNAME"};
		chdir ($cap{"DISTNAME"});
	}


	if (! -f "Makefile") {
		if (-f "makefile") {
			$cap{"MAKEFILE"} = "makefile";
			$makefile = "makefile";
		} elsif (-f "GNUmakefile") {
			$cap{"MAKEFILE"} = "GNUmakefile";
			$cap{"USE_GMAKE"} = "YES";
			$makefile = "GNUmakefile";
		} else {
			print STDERR "No Makefile!\n";
			$cap{"NO_BUILD"} = "YES";
			$cap{"NO_INSTALL"} = "YES";
		}
	} else {
		$makefile = "Makefile";
	}

	my($targets);
	$targets = "";
	$targets = `make -f $makefile -dg1 -q | grep "^[^.#][-_.a-zA-Z ]*:" | awk "{\$1}"` if defined($makefile);
	print $targets;

# at this stage, will be in the right directory

	if (-f "configure") { # ooh, but HAS_CONFIGURE or GNU_CONFIGURE?
		if (`./configure --version` =~ /autoconf/) {
			$cap{"GNU_CONFIGURE"} = "YES";
			$cap{"USE_QT"} = "YES" if (`./configure --help | grep -Fe --with-qt`);
			$cap{"USE_GTK"} = "YES" if (`./configure --help | grep -Fe --with-gtk`);
			$cap{"USE_GNOME"} = "YES" if (`./configure --help | grep -Fe --with-gnome`);
			$cap{"USE_KDE"} = "YES" if (`grep -F "checking for KDE" configure`);
			$cap{"USE_OPENSSL"} = "MUST" if (`grep -F 'configure: error: --with-openssl must be specified' configure`);
			$cap{"USE_OPENSSL"} = "OPTIONAL" if ( (`grep -F 'configure: error: "OpenSSL not in ' configure`) && !($cap{"USE_OPENSSL"}) );
		} else {
			$cap{"HAS_CONFIGURE"} = "YES";
		}
	} else { # maybe they expect us to build configure for them?!
		if (-f "configure.in") {
			$cap{"USE_AUTOCONF"} = "YES";
		} elsif (-f "Configure") {
			$cap{"HAS_CONFIGURE"} = "YES";
			$cap{"CONFIGURE_SCRIPT"} = "Configure";
		}
	}

	if (!$cap{"USE_QT"}) { # haven't seen QT yet, let's try some more
		if ($cap{"USE_KDE"}) {
			$cap{"USE_QT"} = "YES";
		} elsif (-f "automoc") {
			$cap{"USE_QT"} = "YES";
		} elsif (-d "moc") {
			$cap{"USE_QT"} = "YES";
		} elsif (defined($makefile) && -f $makefile && `grep -F -e -lqt $makefile`) { # more insane.
			$cap{"USE_QT"} = "YES";
		}
	}

	if (-f "ltconfig") {
		$cap{"USE_LIBTOOL"} = "YES";
	}

	if (-f "Imakefile") {
		$cap{"USE_IMAKE"} = "YES";
	}
}

sub usage {
	print STDERR <<"EOF";
usage: $0 filename

	generates a ports skeleton for port based on filename

EOF
	exit;
}
