# A debhelper build system class for building PHP PEAR based
# projects.
#
# Copyright: © 2011 Mathieu Parent
# License: GPL-2+

package Debian::Debhelper::Buildsystem::phppear;

use strict;
use warnings;
use Cwd ();
use Debian::Debhelper::Dh_Lib qw(error complex_doit);
use base "Debian::Debhelper::Buildsystem::autoconf";

sub DESCRIPTION {
	"PHP PEAR (package.xml)"
}

sub new {
	my $class=shift;
	my $this=$class->SUPER::new(@_);
	$this->{phppkginfo_path} = '/usr/share/pkg-php-tools/scripts/phppkginfo';
	# Out of source tree building is prefered.
	$this->prefer_out_of_source_building(@_);
	return $this;
}

sub check_auto_buildable {
	my $this=shift;
	return 1 if -e $this->get_sourcepath("package.xml");
	return 1 if -e $this->get_sourcepath("package2.xml");
	return 1 if -e $this->get_sourcepath("channel.xml");
	return 0;
}

# Local functions
sub _shell_exec {
	my $command=shift;
	my $output = `$command`;
	if ($? == -1) {
		error("$command failed to execute: $!");
	}
	elsif ($? & 127) {
		error("$command died with signal ".($? & 127));
	}
	elsif ($? != 0) {
		error("$command returned exit code ".($? >> 8));
	}
	return $output;
}

sub _phppkginfo {
	my $this=shift;
	return _shell_exec($this->{phppkginfo_path}.' '. shift);
}

# Get peardir (relative to sourcedir)
sub _get_peardir {
	my $this=shift;
	return $this->get_sourcedir()."/".$this->{phppkginfo_package}."-".$this->{phppkginfo_version};
}

sub _get_mainpackage {
	my @packages = split("\n", _shell_exec('dh_listpackages'));
	return $packages[0];
}

sub _install_new_files {
	my $this=shift;
	my $old_dir = shift;
	my $new_dir = shift;
	my $target = shift;
	my %old_files = {};
	if (-d $old_dir) {
		opendir(my $old_dh, $old_dir) || error("can't opendir $old_dir: $!");
		%old_files = map { $_ => 1 } grep( $_ ne "." && $_ ne "..", readdir($old_dh));
		closedir $old_dh;
	}
	opendir(my $new_dh, $new_dir) || error("can't opendir $new_dir: $!");
	my %new_files = map { $_ => 1 } grep( $_ ne "." && $_ ne "..", readdir($new_dh));
	closedir $new_dh;
	for (sort keys %new_files) {
		my $old = "$old_dir/$_";
		my $new = "$new_dir/$_";
		my $subtarget = "$target/$_";
		if (-d $new) {
			$this->_install_new_files( $old, $new, $subtarget );
		} elsif( !$old_files{$_} ) {
			if (! -d $new) {
				$this->doit_in_sourcedir("/usr/bin/install",
					 "-T", "-D", "--mode=0644",
					$new, $subtarget);
			}
		}
	}
}

sub _pear_channel_add {
	my $this=shift;
	my $destdir=shift;
	my $builddir=$this->get_builddir() || ".";
	# Create a new PEAR Registry, without ...
	$this->doit_in_sourcedir("/usr/bin/pear",
		"-d", "php_dir=$builddir/without",
		"list-channels");
	# ... and with the channel installed
	$this->doit_in_sourcedir("/usr/bin/pear",
		"-c", "$builddir/.pearrc",
		"-d", "php_dir=$builddir/with",
		"channel-add",
		$this->get_sourcedir()."/channel.xml");
	# Install channel specific files
	$this->_install_new_files("$builddir/without", "$builddir/with", "$destdir/usr/share/php")
}

sub _pear_install {
	my $this=shift;
	my $destdir=shift;
	my @params=@_;
	$this->doit_in_sourcedir("/usr/bin/pear",
		"-c", "debian/pearrc", # Allows local override
		"-d", "download_dir=/tmp",
		"-d", "include_path=/usr/share/php",
		"-d", "php_bin=/usr/bin/php",
		"-d", "bin_dir=/usr/bin",
		"-d", "php_dir=/usr/share/php",
		"-d", "data_dir=/usr/share/php/data",
		"-d", "doc_dir=/usr/share/doc/".$this->_get_mainpackage(),
		"-d", "test_dir=/usr/share/php/tests",
		"install",
		"--offline",
		"--nodeps",
		"-P", $destdir,
		@params,
		$this->_get_peardir()."/package.xml"
	);


}

sub _set_sourcedir {
	my $this=shift;
	my $sourcedir=shift;
	# Get relative sourcedir abs_path (without symlinks)
	my $abspath = Cwd::abs_path($sourcedir);
	if (! -d $abspath || $abspath !~ /^\Q$this->{cwd}\E/) {
		error("invalid or non-existing path to the source directory: ".$sourcedir);
	}
	$this->{sourcedir} = File::Spec->abs2rel($abspath, $this->{cwd});
}

sub pre_building_step {
	my $this=shift;
	my ($step)=@_;
	if (
		(-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml"))
		&& -e $this->get_sourcepath("channel.xml")
	) {
		error("Package can contain only one of package.xml or channel.xml, got both");
	}
	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if (!$this->{phppkginfo_package}) {
			$this->{phppkginfo_package} = $this->_phppkginfo('-d package .');
		}
		if (!$this->{phppkginfo_version}) {
			$this->{phppkginfo_version} = $this->_phppkginfo('-d version .');
		}
		if (!$this->{phppkginfo_package_type}) {
			$this->{phppkginfo_package_type} = $this->_phppkginfo('-d package_type .');
		}
		if ($this->{phppkginfo_package_type} !~ /^(php|extsrc)$/) {
			error('PEAR package type not supported: "'.$this->{phppkginfo_package_type}.'"');
		}
	}
}

sub configure {
	my $this=shift;
	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if (-e $this->get_sourcepath("package2.xml")) {
			$this->doit_in_sourcedir("cp", "package2.xml", $this->_get_peardir()."/package.xml");
		} else {
			$this->doit_in_sourcedir("cp", "package.xml", $this->_get_peardir()."/package.xml");
		}
		# Remove md5sums and sha1sums to allow patching
		$this->doit_in_sourcedir('sed', '-i',
			'-e', 's/md5sum="[^"]*"//',
			'-e', 's/sha1sum="[^"]*"//',
			$this->_get_peardir()."/package.xml");
		if ($this->{phppkginfo_package_type} eq 'extsrc') { # PECL
			$this->_set_sourcedir($this->_get_peardir());
			$this->doit_in_sourcedir('phpize');
			$this->SUPER::configure();
			$this->_set_sourcedir('.');
		}
	}
}

sub build {
	my $this=shift;
	$this->mkdir_builddir();
	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if ($this->{phppkginfo_package_type} eq 'extsrc') { # PECL
			$this->_set_sourcedir($this->_get_peardir());
			$this->SUPER::build();
			$this->_set_sourcedir('.');
		}
	}
}

sub install {
	my $this=shift;
	my $destdir=shift;

	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if ($this->{phppkginfo_package_type} =~ /^(php|extsrc)$/) {
			if ($this->{phppkginfo_package_type} eq 'extsrc') { # PECL
				$ENV{'INSTALL_ROOT'} = $destdir;
				$this->_pear_install($destdir, '--nobuild');
				$this->_set_sourcedir($this->_get_peardir());
				$this->SUPER::install($destdir);
				$this->_set_sourcedir('.');
			} else {
				$this->_pear_install($destdir);
			}
			# remove unwanted files
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/tmp");
			$this->doit_in_sourcedir("rm", "-f", $destdir."/usr/share/php/.filemap");
			$this->doit_in_sourcedir("rm", "-f", $destdir."/usr/share/php/.lock");
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/php/.channels");
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/php/.depdblock");
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/php/.depdb");
			$this->doit_in_sourcedir("rmdir", "--ignore-fail-on-non-empty", $destdir."/usr/share/php/.registry/.channel.pecl.php.net");
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/php/.registry/.channel.doc.php.net");
			$this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/php/.registry/.channel.__uri");
			# workaround pear install which will copy docs file into a subdir
			if (-d $destdir."/usr/share/doc/".$this->_get_mainpackage()."/".$this->{phppkginfo_package}) {
			    $this->doit_in_sourcedir("cp", "-r", $destdir."/usr/share/doc/".$this->_get_mainpackage()."/".$this->{phppkginfo_package}."/.", $destdir."/usr/share/doc/".$this->_get_mainpackage()."/.");
			    $this->doit_in_sourcedir("rm", "-rf", $destdir."/usr/share/doc/".$this->_get_mainpackage()."/".$this->{phppkginfo_package});
			}
			# delete COPYING and LICENSE files and prune empty directories
			if (-d $destdir."/usr/share/doc/") {
				$this->doit_in_sourcedir("find", $destdir."/usr/share/doc/", "-type", "f", "-name", "COPYING", "-delete");
				$this->doit_in_sourcedir("find", $destdir."/usr/share/doc/", "-type", "f", "-name", "LICENSE", "-delete");
				$this->doit_in_sourcedir("find", $destdir."/usr/share/doc/", "-type", "f", "-empty", "-delete");
				$this->doit_in_sourcedir("find", $destdir."/usr/share/doc/", "-type", "d", "-empty", "-delete");
			}
			# Remove tests files
			if (!$ENV{PHPPEAR_KEEP_TESTS}) {
				$this->doit_in_sourcedir('rm', '-rf', $destdir.'/usr/share/php/tests');
			}

			# add package.xml and changelog to doc dir
			$this->doit_in_sourcedir("mkdir", "-p", $destdir."/usr/share/doc/".$this->_get_mainpackage());
			$this->doit_in_sourcedir("cp", "package.xml", $destdir."/usr/share/doc/".$this->_get_mainpackage());
			if (-e $this->get_sourcepath("package2.xml")) {
				$this->doit_in_sourcedir("cp", "package2.xml", $destdir."/usr/share/doc/".$this->_get_mainpackage());
			}
			complex_doit($this->{phppkginfo_path}." -d changelog ".$this->get_sourcedir()." > ".$destdir."/usr/share/doc/".$this->_get_mainpackage()."/changelog");
		}
	}
	if (-e $this->get_sourcepath("channel.xml")) {
		$this->_pear_channel_add($destdir);
	}
}

sub test {
	my $this=shift;
	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if ($this->{phppkginfo_package_type} eq 'extsrc') { # PECL
			$ENV{'NO_INTERACTION'} = '1';
			$this->SUPER::test();
		}
	}
}

sub clean {
	my $this=shift;
	if (-e $this->get_sourcepath("package.xml") || -e $this->get_sourcepath("package2.xml")) {
		if ($this->{phppkginfo_package_type} eq 'extsrc') { # PECL
			$this->_set_sourcedir($this->_get_peardir());
			$this->SUPER::clean();
			$this->doit_in_sourcedir('phpize', '--clean');
			$this->_set_sourcedir('.');
		}
		$this->doit_in_sourcedir("rm", "-f", $this->_get_peardir()."/package.xml");
	}
	$this->rmdir_builddir();
}

1
