package Debian::Debhelper::Buildsystem::leiningen;

=head1 NAME

leiningen -- debhelper build system class for Clojure packages

=head1 DESCRIPTION

See L<dh-clojure-lein(7)> for additional information.

=head1 SEE ALSO

L<dh-clojure-lein(7)>, L<dh(1)>, and L<lein(1)>

=cut

use Cwd;
use File::Path qw(remove_tree);
use strict;
use warnings;
use parent qw(Debian::Debhelper::Buildsystem);
use Debian::Debhelper::Dh_Lib;

sub DESCRIPTION {
    'Leiningen'
}

sub DEFAULT_BUILD_DIRECTORY {
    'target'
}

sub check_auto_buildable {
    my $this = shift;
    return $this->get_sourcepath('project.clj') ? 0 : 1;
}

my $lein = '/usr/bin/lein';
my $reltmp = 'debian/dh-clojure/tmp';
my $abstmp = getcwd . '/debian/dh-clojure/tmp';
my $local_repo = "$abstmp/maven-repo";

sub new {
    my $class = shift;
    my $this  = $class->SUPER::new(@_);

    # key project attributes
    $this->{group} = $this->_get_project('group');
    $this->{name} = $this->_get_project('name');
    $this->{version} = $this->_get_project('version');

    # path to jar artifacts
    $this->{artifact} = "target/$this->{name}-$this->{version}.jar";
    $this->{test_artifact} = "target/test/$this->{name}-$this->{version}-test.jar";

    # Absolute because (at least) subprocs might change the parent
    # directory and run lein, e.g. DH_CLOJURE_LEIN_CREATE_MAVEN_REPO.
    $ENV{'LEIN_HOME'} = "$abstmp/lein-home";

    # lein may not attempt to access the network
    # (Debian policy, section 4.9)
    $ENV{'LEIN_OFFLINE'} = 'true';

    return $this;
}

sub _get_project {
    my $this = shift;
    my ($property) = @_;

    my $cmd = "DH_CLOJURE_LEIN_BUILD=true $lein"
        . " update-in : assoc :local-repo '\"$local_repo\"' --"
        . " update-in :$property println --"
        . ' version';

    my $output = `$cmd`;
    if ($?) {
        print STDERR "$output\n"; # because a stacktrace shows up there
        error_exitcode($cmd)
    }

    my ($value) = split(/\n/, $output);
    return $value;
}

sub _do_lein {
    my $this = shift;
    doit({'update_env' => {'DH_CLOJURE_LEIN_BUILD' => 'true'}},
         $lein, 'update-in', ':', 'assoc', ':local-repo', "\"$local_repo\"",
         '--', @_);
}

sub _create_maven_repo {
    # This function also runs at the start of clean() to provide the
    # normal environment for lein clean (including dh-clojure-lein),
    # so it should be careful to "always" work.
    doit('mkdir', '-p', $abstmp);
    # This is currently for dh-clojure internal use only.
    my $override = $ENV{'DH_CLOJURE_LEIN_CREATE_MAVEN_REPO'};
    if ($override) {
        doit($override, '/usr/share/maven-repo', "$abstmp/maven-repo");
    } else {
        remove_tree("$abstmp/maven-repo");
        doit('ln', '-sf', '/usr/share/maven-repo', "$abstmp/maven-repo");
    }
}

sub configure {
    _create_maven_repo();
}

sub build {
    my $this = shift;

    # generate the pom artifact
    $this->_do_lein('pom', "$reltmp/pom.xml"); # only accepts relative paths

    # generate the jar artifact
    $this->_do_lein('jar');

    doit('test', '-f', $this->{artifact});
}

sub install {
    my $this = shift;
    my $pomsfile = $dh{MAINPACKAGE} . '.poms';
    my $usj_name = defined($ENV{DH_CLOJURE_USJ_NAME})
        ? $ENV{DH_CLOJURE_USJ_NAME} : $this->{name};

    # Install build artifacts to /usr/share/{java,maven-repo}.
    # This replaces the call to mh_install because we're unable to
    # pass the --no-rules switch to it, so that it avoids messing
    # with dependency versions in the pom file. The sequence below
    # must be followed exactly because mh_installjar consumes the
    # pom.properties fileis generated by mh_installpom.
	# -> upstream versioned
    doit('mh_installpom', "-p$dh{MAINPACKAGE}", '--no-rules', '--keep-pom-version', "$reltmp/pom.xml");
    doit('mh_installjar', "-p$dh{MAINPACKAGE}", '--skip-clean-pom', '--java-lib', "--usj-name=$usj_name", "$reltmp/pom.xml", $this->{artifact});
    if (defined($ENV{DH_CLOJURE_INSTALL_TEST_JAR})
        && $ENV{DH_CLOJURE_INSTALL_TEST_JAR} eq 'true') {
        doit('mh_installjar', "-p$dh{MAINPACKAGE}", '--skip-clean-pom', '--java-lib', '--classifier=test', "--usj-name=$usj_name-test", "$reltmp/pom.xml", $this->{test_artifact});
	}
	# -> debian versioned
    doit('mh_installpom', "-p$dh{MAINPACKAGE}", '--no-rules', '--set-version=debian', "$reltmp/pom.xml");
    doit('mh_installjar', "-p$dh{MAINPACKAGE}", '--skip-clean-pom', '--java-lib', "--usj-name=$usj_name", "$reltmp/pom.xml", $this->{artifact});
    if (defined($ENV{DH_CLOJURE_INSTALL_TEST_JAR})
        && $ENV{DH_CLOJURE_INSTALL_TEST_JAR} eq 'true') {
        doit('mh_installjar', "-p$dh{MAINPACKAGE}", '--skip-clean-pom', '--java-lib', '--classifier=test', "--usj-name=$usj_name-test", "$reltmp/pom.xml", $this->{test_artifact});
    }

    # Reset debian.* POM properties because they're not generated
    # properly due to the "uncoventional" sequence above.
    my $maven_group_path = $this->{group} =~ tr/./\//r;
    my $maven_path = "debian/$dh{MAINPACKAGE}/usr/share/maven-repo/$maven_group_path/$this->{name}";
    foreach ("$maven_path/debian/$this->{name}-debian.pom", "$maven_path/$this->{version}/$this->{name}-$this->{version}.pom") {
        print $_;
        print ('xmlstarlet', 'ed', '-L', '-N', 'x="http://maven.apache.org/POM/4.0.0"');
        doit('xmlstarlet', 'ed', '-L', '-N', 'x=http://maven.apache.org/POM/4.0.0',
            '-d', '/x:project/x:properties/*',
            '-s', '/x:project/x:properties', '-t', 'elem', '-n', 'debian.hasPackageVersion',
            '-s', '/x:project/x:properties', '-t', 'elem', '-n', 'debian.originalVersion', '-v', $this->{version},
            '-s', '/x:project/x:properties', '-t', 'elem', '-n', 'debian.packageName', '-v', $dh{MAINPACKAGE},
            $_);
    }

    # Since we handle our own .pom files, we don't want
    # mh_resolve_dependencies to touch them, and right now it will
    # (via mh_patchpoms) unless .debianVersion exists.  It's easy to
    # see where that happens in the current source, but it's not
    # documented as far as I've seen, and so we may eventually want to
    # consider pursuing a --no-patch option or something.
    doit('touch', '.debianVersion');

    # Write a debian/<package>.poms file for mh_resolve_dependencies,
    # as there doesn't seem to be any other way for it to discover
    # our POM files.
    my $pomsline = "$reltmp/pom.xml";
    complex_doit("echo '$pomsline' > debian/$pomsfile");

    # This generates our ${maven:Depends} substvar.
    doit('mh_resolve_dependencies', '--non-interactive', '--offline', '--build', "-p$dh{MAINPACKAGE}", '--non-explore');
}

sub test {
    my $this = shift;
    $this->_do_lein('test');
}

sub clean {
    my $this = shift;
    my $pomsfile = $dh{MAINPACKAGE} . '.poms';

    _create_maven_repo(); # provide dh-clojure-lein
    $this->_do_lein('clean');
    doit('rm', '-f', 'debian/' . $pomsfile, '.debianVersion', '.lein-failures');
    remove_tree($abstmp);
    doit('rmdir', '--ignore-fail-on-non-empty', 'debian/dh-clojure');
}

1;
