#!/usr/bin/perl

# Copyright (C) 2009,2010  Neil Williams <codehelp@debian.org>

# This package is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# using dpkg-checkbuilddeps means we don't have to deal with multiline
# Depends and only missing packages are checked.

use strict;
use warnings;
use File::Basename;
use POSIX qw(locale_h);
use Term::ANSIColor qw(:constants);
use Locale::gettext;
use vars qw/ $progname $ourversion $name $deps $ret @list %res @cmd 
 @series $splice $constraint $version $check $pkg $policy @bits $limit
 $c $msg $use_xcontrol $vendor $tools @dependencies @aptlist $use_sudo
 $arch $dryrun $cmd /;

setlocale(LC_MESSAGES, "");
textdomain("xapt");

$progname = basename($0);
$ourversion = &scripts_version();

while( @ARGV ) {
	$_= shift( @ARGV );
	last if m/^--$/;
	if (!/^-/) {
		unshift(@ARGV,$_);
		last;
	}
	elsif (/^(-\?|-h|--help|--version)$/) {
		&usageversion();
		exit 0;
	}
	elsif (/^(-a|--arch)$/) {
		$arch = shift(@ARGV);
		$use_xcontrol++;
	}
	elsif (/^(--use-sudo)$/) {
		$use_sudo++;
	}
	elsif (/^(-n|--dry-run)$/) {
		$dryrun++;
	}
	else {
		&usageversion();
		die RED, "$progname: "._g("Unknown option")." $_.\n", RESET;
	}
}

$name=`dpkg-parsechangelog|grep Source:`;
chomp ($name);
$name =~ s/Source: //;
if (defined $use_xcontrol)
{
	open (XC, "<debian/xcontrol");
	my @xc=<XC>;
	close (XC);
	# allow multi-lines
	my $str = join ("", @xc);
	$str =~ s/\n / /g;
	$str =~ s/  / /g;
	my @long = split ("\n", $str);
	foreach my $line (@long)
	{
		if ($line =~ /^Build-Depends-Tools: /)
		{
			$line =~ s/^Build-Depends-Tools: //;
			$tools = $line;
			
		}
		if ($line =~ /^Build-Depends: /)
		{
			$line =~ s/^Build-Depends: //;
			$deps = $line;
		}
	}
	print "deps=$deps\n";
	print "tools=$tools\n";

	@list=split(/ /, $tools);
	%res=();
	@cmd=();
	@series=();
	$c=0;
	foreach $splice (@list)
	{
		chomp ($splice);
		$res{$splice} = $splice;
		push @series, $splice;
		if (($splice =~ /^\(/) and ($c > 0))
		{
			delete $res{$splice};
		}
		if (($splice =~ /\).?$/))# and ($c > 1))
		{
			delete $res{$splice};
		}
		$c++;
	}
	my %sort=();
	foreach my $tool (sort keys %res) {
		$sort{$tool}++;
	}
	$deps =~ s/,//g;
	my $sudo = (defined $use_sudo) ? "sudo " : "";
	print GREEN;
	printf (_g("Checking that build tools for %s are installed:\n"), $name);
	print RESET;
	if (defined $dryrun)
	{
		print "${sudo}apt-get install ".join(" ", sort keys %sort)."\n";
	}
	else
	{
		my $cmd = "${sudo}apt-get install ".join(" ", sort keys %sort);
		system ("$cmd");
	}
}
else
{
	$deps=`dpkg-checkbuilddeps 2>&1`;
}
@bits=();
$ret = `echo $?`;
$ret /= 256;
# we expect a value of 1 if build-deps are needed, 0 is OK too.
$msg = sprintf(_g("%s: dpkg-checkbuilddeps failed with return value: %s\n"),
	$progname, $ret);
die (RED, $msg, RESET, "\n")
	if (($ret != 1) and ($ret != 0));
$deps =~ s/dpkg-checkbuilddeps: Unmet build dependencies: //;
@list=split(/ /, $deps);
%res=();
@cmd=();
@series=();
$c=0;
foreach $splice (@list)
{
	chomp ($splice);
	$res{$splice} = $splice;
	push @series, $splice;
	if (($splice =~ /^\(/) and ($c > 0))
	{
		$res{$series[$c - 1]} .= " $splice";
		delete $res{$splice};
	}
	if (($splice =~ /\)$/) and ($c > 1))
	{
		$res{$series[$c - 2]} .= " $splice";
		delete $res{$splice};
	}
	$c++;
}

foreach $constraint (sort values %res)
{
	undef $limit;
	undef $version;
	@bits=split(/ /, $constraint);
	$pkg = $bits[0];
	next if (not defined $pkg);
	$msg = sprintf (_g("%s: Must have apt to proceed!\n"), $progname);
	die (RED, $msg, RESET) if ($pkg eq "apt");
	if (defined $bits[1])
	{
		$limit = $bits[1];
		$limit =~ s/\(//;
	}
	if (defined $bits[2])
	{
		$version = $bits[2];
		$version =~ s/\)//;
	}
	$policy=`LC_ALL=C apt-cache policy $pkg 2>/dev/null|grep Candidate|cut -d':' -f2-3|tr -d ' '`;
	$msg = sprintf (_g("%s: Failed to read apt-cache policy for '%s'\n"),
		$progname, $pkg);
	die (RED, $msg, RESET)
		if ($policy eq "\n");
	chomp ($policy);
	if ((defined $version) and (not defined $use_xcontrol))
	{
		$check = `dpkg --compare-versions $policy \"$limit\" $version ; echo $?`;
		chomp ($check);
		push @cmd, $pkg if ($check == 0);
		next;
	}
	push @cmd, $pkg;
}
if ((scalar @cmd) > 0)
{
	if (defined $use_xcontrol)
	{
		print GREEN;
		printf (_g("%s needs cross dependencies installed:\n"), $name);
		print RESET;
		if (defined $dryrun)
		{
			print "xapt -a $arch ";
			print join (" ", @cmd)."\n";
		}
		else
		{
			my $cmd = "xapt -a $arch ".join (" ", @cmd);
			system ("$cmd");
		}
	}
	else
	{
		print GREEN;
		printf (_g("%s needs dependencies installed:\n"), $name);
		print RESET;
		if (defined $dryrun)
		{
			print "apt-get install ";
			print join (" ", @cmd);
			print "\n";
		}
		else
		{
			my $cmd = "apt-get install " . join (" ", @cmd);
			system ("$cmd");
		}
	}
}
else
{
	if (defined $use_xcontrol)
	{
		print GREEN;
		printf (_g("No cross dependencies to install for %s\n"), $name);
		print RESET;
	}
	else
	{
		print GREEN;
		printf (_g("No build dependencies to install for %s\n"), $name);
		print RESET;
	}
}

sub usageversion {
	printf STDERR (_g("
%s version %s

Usage:
 %s [-a|--arch] [--use-sudo]
 %s -?|-h|--help|--version

Options:
 -a|--arch:          Read build dependencies from debian/xcontrol
                      and install cross packages for the specified arch.
 --use-sudo:         Call apt-get using sudo.

When used outside a chroot or as a user, set the --use-sudo option.

"), $progname, $ourversion, $progname, $progname);
}

sub scripts_version {
	my $query = `dpkg-query -W -f='\${Version}' emdebian-buildsupport 2>/dev/null`;
	(defined $query) ? return $query : return "";
}

sub _g {
	return gettext(shift);
}

=pod

=head1 NAME

embuilddeps - handle native and cross build-dependency installation.

=head1 Usage

 embuilddeps [-a|--arch] [--use-sudo]

 embuilddeps -?|-h|--help|--version

=head1 Options

 -a|--arch:          Read build dependencies from debian/xcontrol
                      and install cross packages for the specified arch.
 --use-sudo:         Call apt-get using sudo.

When used outside a chroot or as a user, set the C<--use-sudo> option.

=head1 Description

C<embuilddeps> is a simple build dependency checker for Emdebian Crush.
Native build dependencies are checked using the F<debian/control> file
in the source package being built and installed with C<apt-get>.

Cross build dependencies are checked using a F<debian/xcontrol> file,
added to the package for Emdebian Crush. Packages that need to exist
as cross packages need to be specified in the C<Build-Depends> line
of the F<debian/xcontrol> file. Packages that only need to exist as
native packages, e.g. build tools like autoconf or CDBS, can only
be specified in C<Build-Depends-Tools>.

Currently, C<embuilddeps> still uses C<apt-cross> for cross build
dependencies but a replacement method is being developed.

=cut
