# A debhelper build system class for handling cabalized haskell packages.
#
# Copyright (C) 2015  Bogatov Dmitry <KAction@gnu.org>
#
# This program 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/>.

=pod

=head1 NAME

dh-haskell -- debhelper build system class for cabal-based haskell packages

=head1 DESCRIPTION

The dh-haskell package provides a build system for debhelper which can be used
in following way:

 %:
 	dh $@ --build-system=haskell

Usually it is all you need to know, and maybe you may want to adjust
DEB_BUILD_OPTIONS I<noopt> and I<nocheck>.  If you want to know precisely what
and how is performed, read on implementation section.

=head1 IMPLEMENTATION

=cut

package Debian::Debhelper::Buildsystem::haskell;
use strict;
use warnings;
use v5.14;

use constant USE_LEGACY => 1;
if (USE_LEGACY) {

    # use pragma is evaluated unconditionally, but visually it helps
    # to view code, that should be removed.
    use Debian::Debhelper::Buildsystem::haskell::legacy qw(@PROVIDED_BY_GHC);
}

use HTML::LinkExtractor;
use Debian::Debhelper::Dh_Lib;
use Parse::DebianChangelog;
use Parse::DebControl;
use parent qw(Debian::Debhelper::Buildsystem);
use Readonly;
use File::Find;
use File::chdir;
use List::MoreUtils qw(any uniq);
use Carp::Assert;

=pod

=head2 HASKELL COMPILER

The dh-haskell package supports two haskell compilers -- B<ghc> and B<ghcjs>,
with only B<ghc> is present in official Debian repositories. Each of them have
following different properties, that should be well-known to anyone familiar
with I<configure> scripts.

=over

=item I<bin>

name of compiler (for example, B<ghc>)

=item I<haddoc>

absolute path to corresponding haddock binary (different compilers have
different haddock binary)

=item I<prefix>

Unless overridden, every installation path is under prefix path.

=item I<libdir>

Interface files (for example F<Writer.hi>) and shared library (for example
F<libHSmtl-2.1.3.1-ghc7.8.4.so>) are installed under libdir path.

=item I<pkgdir>

Every package should provide configuration file, like F<mtl-2.1.3.1.conf> which
is used by tools, like B<ghc-pkg>. It is installed under pkgdir path.

=back

Actual values for these paths for each I<compiler> can be found in source of
functions B<compiler>_I<compiler>. You are not meant to change them, since for
packages to be well-used together, during compilation and installation they
all should be configured with same paths.

=cut

sub compiler_ghc {
    return {
        bin     => 'ghc',
        prefix  => '/usr',
        libdir  => '/usr/lib/haskell-packages/ghc/lib',
        pkgdir  => '/var/lib/ghc/package.conf.d',
        haddock => '/usr/bin/haddock'
    };
}

sub compiler_ghcjs {

    # If these commands fail, it would require to read this source to understand
    # what is happening. But such basic commands should not fail anyway.

    my $cpu               = `ghc -e "putStr System.Info.arch"` or die $!;
    my $os                = `ghc -e "putStr System.Info.os"`   or die $!;
    my $ghcjs_version     = `ghcjs --numeric-ghcjs-version`    or die $!;
    my $ghcjs_ghc_version = `ghcjs --numeric-ghc-version`      or die $!;

    # Nothing of great meaning. Just to fit into 80 column.
    my $pkgdir_part = "$cpu-$os-$ghcjs_version-$ghcjs_ghc_version";

    return {
        bin     => 'ghcjs',
        prefix  => '/usr/lib/ghcjs',
        libdir  => '/usr/lib/ghcjs/.cabal/lib',
        pkgdir  => "/usr/lib/ghcjs/.ghcjs/$pkgdir_part/ghcjs/packages.conf.d",
        haddock => '/usr/bin/haddock-ghcjs'
    };
}

=pod

What compiler to use is decided by inspecting names of binary packages, which
have names is form libI<compiler>-foo-(dev|doc|prof). Multiple compilers
for same source package are NOT supported.

=cut

sub detect_haskell_compiler {
    my %compilers;

    for my $bin_pkg ( @{ $dh{DOPACKAGES} } ) {
        if ( $bin_pkg =~ /^lib([[:lower]]+)-[-[:lower:]]+-(?:doc|dev|prof)$/ ) {
            $compilers{$1} = 1;
        }
    }
    my @compilers = keys %compilers;

    if ( @compilers > 1 ) {
        error('Multiple compiler packages are not supported.');
    }

    # If no compilers detected, fall back on ghc.
    my $compiler = @compilers ? $compilers[0] : 'ghc';

    given ($compiler) {
        when ('ghc')   { return compiler_ghc; }
        when ('ghcjs') { return compiler_ghcjs; }
        default        { error("Compiler `$compiler' is not supported"); }
    }
}

# These variables are global since every stage (configure, build, test, clean) need information
# about haskell compiler (HC). HC is hash reference, HC_BIN is actual name of compiler and
# HS_BUILD_DIR is global since it avoid code duplication, since every call to `DEB_SETUP_BIN_NAME'
# need to pass builddir option.

Readonly my $SETUP_BIN_NAME => 'debian/haskellbuild.setup';
Readonly my $HC             => detect_haskell_compiler;
Readonly my $HC_BIN         => $HC->{bin};

# HC_BUILD_DIR is written into haskell interface files

Readonly my $HC_BUILD_DIR => USE_LEGACY
  ? "dist-$HC_BIN"
  : "debian/dist-$HC_BIN";

# Abstract away $SETUP_BIN_NAME and $HC_BUILD_DIR paths.
sub run_setup {
    my $command = shift;
    my @args    = @_;
    print_and_doit( $SETUP_BIN_NAME, $command, "--builddir=$HC_BUILD_DIR",
        @args );
}

=pod

=head2 PREPARATION

As mimimal safety belt against cryptic errors, dh-haskell refuses to try build package if
it fails to find cabal file. If it is found, it is compiled into binary under F<debian/>,
which is used to orchestrate build process.

=cut

sub check_auto_buildable {
    my $this      = shift;
    my $sourcedir = $this->get_sourcedir;
    return <$sourcedir/*.cabal>;
}

sub compile_setup_step {
    my $this = shift;

    for my $setup_src_name ( 'Setup.hs', 'Setup.lhs' ) {
        if ( -e $setup_src_name ) {
            print_and_doit( 'ghc', '--make', '-threaded', $setup_src_name, '-o',
                $SETUP_BIN_NAME );
            return;
        }
    }
    error('Failed to find Setup.hs or Setup.lhs.');
}

=pod

=head2 CONFIGURATION

Obliviously, installation paths of package depends on it's upstream name
and version. Upstream name is detected by inspecting F<debian/copyright>
DEP5 file, falling back on source package name. Upstream version
is deduced from debian version in F<debian/changelog>.

=cut

sub upstream_name {
    my $result = eval {
        my $parser = new Parse::DebControl;
        my $data   = $parser->parse_file("debian/copyright");
        return $data->[0]->{'Upstream-Name'};
    };
    return $result unless $@;

    for (<*.cabal>) {
        open( my $fd, "$_" ) or die $!;
        while (<$fd>) {
            return $1 if (/^[Nn]ame:\s*(.*)\s*$/);
        }
        close($fd);
    }
    error("Failed to deduce upstream name");
}

sub package_name {
    my $parser       = new Parse::DebControl;
    my $data         = $parser->parse_file("debian/control");
    my $src_pkg      = $data->[0]->{Source};
    my $package_name = $src_pkg;

    $package_name =~ s/^haskell-//;
    return $package_name;
}

sub upstream_version {
    my $chglog =
      Parse::DebianChangelog->init( { infile => "debian/changelog" } );

    my $debian_version   = $chglog->dpkg->{Version};
    my $upstream_version = $debian_version;

    $upstream_version =~ s/(\+dfsg)?-\d+$//;

    return $upstream_version;
}

# These variables are global to make easier split function,
# building configure arguments.
Readonly my $PACKAGE_NAME     => package_name;
Readonly my $UPSTREAM_NAME    => upstream_name;
Readonly my $UPSTREAM_VERSION => upstream_version;

# Handle flags in DEB_BUILD_OPTIONS environment variable (see 4.9.1).
# If flag is absent, return false.
# If flag is present, but have not argument, return true.
# If flag is present and have argument, return argument.
sub deb_buildopt {
    my $flag    = shift;
    my $options = $ENV{DEB_BUILD_OPTIONS};

    if ( $options =~ /(?:\s|^)$flag(?:\s|$)/ ) {
        return 1;
    }

    if ( $options =~ /(?:\s|^)$flag=(\S+)(?:\s|$)/ ) {
        return $1;
    }

    return 0;

}

=pod

Package is configured with profiling enabled only if there is binary package with name like
libI<compiler>-I<pkgname>-prof.

=cut

sub detect_is_profiling_required {
    for my $bin_pkg ( @{ $dh{DOPACKAGES} } ) {
        if ( $bin_pkg =~ /^lib[-[:lower:]]+-prof$/ ) {
            return 1;
        }
    }
    return 0;
}

=pod

If compiler is ghc, LDFLAGS, suggested by dpkg-buildpackage are forwarded to
ghc, that harden generated binaries and fix corresponding lintian warning.

=cut

sub ghc_linker_flags {
    my $link_flags = `dpkg-buildflags --get LDFLAGS` or die $!;
    chomp $link_flags;
    my @link_flags = split " ", $link_flags;
    return map "--ghc-option=-optl$_", @link_flags;
}

sub configure {
    my $this = shift;

    $this->compile_setup_step;

    my $profiling_required = detect_is_profiling_required;

    my @configure_args;
    push @configure_args, "--$HC_BIN";
    push @configure_args, '-v2';
    push @configure_args, "--package-db=$HC->{pkgdir}";
    push @configure_args, "--prefix=$HC->{prefix}";
    push @configure_args, "--libdir=$HC->{libdir}";
    push @configure_args, '--datadir=/usr/share';

=pod

If for some reason ghci is not available, preprocessor token C<DEBIAN_NO_GHCI> is
defined.

=cut

    if ( $HC_BIN eq 'ghc' ) {
        push @configure_args, ghc_linker_flags;
        if ( not -e '/usr/bin/ghci' ) {
            push @configure_args, '--ghc-option=-DDEBIAN_NO_GHCI';
        }

=pod

Value of environment variable C<GHC_CONFIGURE_ARGS> is passed unmodified at configuration
stage. It may be useful, if you need enable or disable flags due some complicated logic,
implemented in F<debian/rules>. B<Please>, don't abuse it.

=cut

        push @configure_args, $ENV{GHC_CONFIGURE_ARGS}
          if $ENV{GHC_CONFIGURE_ARGS};
    }

    my $pkg_id      = "$PACKAGE_NAME-$UPSTREAM_VERSION";
    my $haddock_dir = "/usr/lib/$HC_BIN-doc/haddock/$pkg_id/";
    push @configure_args, "--haddockdir=$haddock_dir";

    push @configure_args, "--datasubdir=$PACKAGE_NAME";

    my $doc_pkgname = "lib$HC_BIN-$PACKAGE_NAME-doc";
    my $html_dir    = "/usr/share/doc/$doc_pkgname/html/";
    push @configure_args, "--htmldir=$html_dir";

    if ($profiling_required) {
        push @configure_args, "--enable-library-profiling";
    }

=pod

If options I<noopt> or I<nocheck> are present in C<DEB_BUILD_OPTIONS>,
then optimization or tests are disabled, correspondingly.

=cut

    if ( deb_buildopt 'noopt' ) {
        push @configure_args, '--disable-optimization';
    }

    if ( deb_buildopt 'nocheck' ) {
        push @configure_args, '--disable-tests';
    }
    else {
        push @configure_args, '--enable-tests';
    }

    run_setup( 'configure', @configure_args );
}

sub DESCRIPTION {
    return 'cabalized Haskell packages';
}

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

    $this->enforce_in_source_building;

    return $this;
}

=pod

=head2 BUILDING

Building of library is trivial and is essence equivalent to C<cabal build>.  If
haddock binary, corresponding to current compiler is present
(B<FIXME: When it does not?>), it is used to also build hyperlinked documentation.

=cut

sub build {
    run_setup('build');

    my $haddock_bin = $HC->{haddock};
    if ( -x $haddock_bin ) {
        my @haddock_args = qw/--hoogle --html --hyperlink-source/;
        push @haddock_args, "--with-haddock=$haddock_bin";
        push @haddock_args, "--with-ghc=$HC_BIN";
        run_setup( 'haddock', @haddock_args );
    }
}

=pod

=head2 TESTING

Unless tests were disabled at configuration stage, they are run.

=cut

sub test {
    if ( not deb_buildopt 'nocheck' ) {
        run_setup( 'test', '--show-details=always' );
    }
}

=pod

=head2 INSTALLATION

First, compiled objects are copied into temporary directory, and then
dispatached, to which package what belong. See C<do_dispatch> in source for
exact filename patterns. If file does not match any pattern, it is installed
into non-library binary package, if any, or in I<-dev> otherwise.

=cut

Readonly my $TMP_INSTALL_DIR => "debian/tmp-install-$HC_BIN";

sub do_dispatch {
    my %INSTALL = ( dev => [], prof => [], doc => [], bin => [], );
    my $dispatch = sub { push $INSTALL{ @_[0] }, $_ };

    my $dispatcher = sub {
        no strict 'subs';    # barewords sometimes are neat
        return unless -f;

        given ($File::Find::name) {

# .haddock file is installed by cabal(1) under /usr/share/doc and under /usr/var
# at same time. We skip /usr/share/doc/.*/html one.
            when (/\/html\/.*\.haddock$/)             { "skip it" }
            when (/\/html\//)                         { $dispatch->(doc) }
            when (/\.haddock$/)                       { $dispatch->(doc) }
            when (/$HC->{libdir}.*(?:_p\.a|\.p_hi)$/) { $dispatch->(prof) }
            when (/$HC->{libdir}/)                    { $dispatch->(dev) }
            when (m#/usr/bin/.+$#)                    { $dispatch->(bin) }
            when (/LICENSE$/)                         { "skip it" }
            when (/COPYING$/)                         { "skip it" }
            when (/TODO$/)                            { "skip it" }
            when (/README$/)                          { "skip it" }
            when (/COPYRIGHT$/)                       { "skip it" }
            when (/license.txt$/)                     { "skip it" }
            default { error("Unhandled file: $File::Find::name") }
        }
    };
    File::Find::find( $dispatcher, $TMP_INSTALL_DIR );
    return %INSTALL;
}

# Enumerate binary packages. FIXME: document properly.
our $binpkg;
our $type;

sub binpkg {
    my $type = shift;
    return "lib$HC_BIN-$PACKAGE_NAME-$type";
}

sub for_binpkg {
    my $fn = pop @_;
    my @args = @_ ? @_ : qw(dev doc prof);
    my @result;

    for (@args) {
        local $binpkg = binpkg $_;
        local $type   = $_;
        if ( $binpkg ~~ @{ $dh{DOPACKAGES} } ) {
            push @result, $fn->();
        }
    }
    return @result;
}

sub exec_binpkg {
    my @exec_binpkgs = grep { !/^lib.*(doc|dev|prof)$/ } @{ $dh{DOPACKAGES} };

    given ( scalar @exec_binpkgs ) {
        when (0) { return undef; }
        when (1) { return $exec_binpkgs[0]; }
        default {
            warn("More than one non-libarary binary packages: @exec_binpkgs");
            warn("To install executables manual intervention is required");
            warn("Probably you should see dh_install(1)");
            return undef;
        }
    }
}

sub mirror_install_file {
    my $src = shift || $_;
    ( my $dest = $src ) =~ s@$TMP_INSTALL_DIR@debian/$binpkg@;
    install_dir( dirname($dest) );
    install_file( $src, $dest );
}

sub do_install {
    my %INSTALL = @_;

    if ( @{ $INSTALL{bin} } ) {
        my $exec_binpkg = exec_binpkg;
        if ( not defined $exec_binpkg ) {
            warn("Executables will not be installed");
        }
        else {
            local $binpkg = $exec_binpkg;
            mirror_install_file for @{ $INSTALL{bin} };
        }
    }

    for_binpkg sub { mirror_install_file for @{ $INSTALL{$type} } };

    # FIXME: find more principled solution for this exception
    $HC_BIN eq 'ghc' && for_binpkg qw(doc), sub {
        my $src  = "/usr/share/doc/$binpkg/html/$UPSTREAM_NAME.txt";
        my $dest = "/usr/lib/ghc-doc/hoogle/$binpkg.txt";
        make_symlink( $dest, $src, tmpdir($binpkg) );
    };

}

=pod

Separately pkg-config file is generated (ex. F<bool-extras-0.4.0.conf>) and
installed for dev binary package under compiler pkgdir. This file contains
package build indentifier, used to generate C<haskell:Provides> substitute
variable. It also contains dependencies, used to generate C<haskell:Depends>
for I<dev> and I<prof> packages, and C<haskell:Recommends> for I<doc> packages.

=cut

sub install_pkg_config {
    run_setup( 'register', '--gen-pkg-config' );

    my @candidates = <*.conf>;

    given ( scalar @candidates ) {

        # Package have no library, no pkg-config file generated
        when (0) { return undef }
        when (1) {
            my $pkg_config      = $candidates[0];
            my $pkg_config_dir  = tmpdir( binpkg 'dev' ) . "$HC->{pkgdir}";
            my $pkg_config_dest = "$pkg_config_dir/$pkg_config";

            install_dir($pkg_config_dir);
            install_file( $pkg_config, $pkg_config_dest );
            print_and_doit( 'rm', '-f', $pkg_config );

            return $pkg_config_dest;
        }
        default { error("More than one pkg-config file: @candidates"); }
    }
}

=pod

Such cabal build identifiers looks like bool-extras-I<HASH> or base-I<HASH>,
where I<HASH> is 32 heximal digits string. For purposed of debian packaging, we
convert it to something like C<libghc-bool-extras-(dev|prof)-I<SHORT_HASH>>,
where I<SHORT_HASH> is first 5 digits of I<HASH>.

=cut

sub cabal_id_to_virtual_package {
    my ( $flavor, $cabal_id ) = @_;
    my ( $name, $version, $short_hash ) =
      ( $cabal_id =~ m/^(.*)-([.0-9]+)-([0-9a-f]{5})[0-9a-f]{27}$/ );
    $name =~ s/(.*)/\L\1/;
    shouldnt( $flavor, 'doc' );

    return "lib$HC_BIN-$name-$flavor-$version-$short_hash";
}

sub substitute_provides {
    my ($config) = @_;
    my $id = $config->[0]->{'id'};

    for_binpkg qw(dev prof), sub {
        my $provides = cabal_id_to_virtual_package( $type, $id );
        addsubstvar( $binpkg, "haskell:Provides", $provides );
    };

    if (USE_LEGACY) {
        for_binpkg qw(dev), sub {
            addsubstvar( $binpkg, 'haskell:ghc-package', $id );
        };
    }
}

# Parse html file and depends on packages, whose files are actually linked to.
sub doc_package_dependencies {
    my $LX   = new HTML::LinkExtractor;
    my $root = tmpdir( binpkg 'doc' );
    my @files;
    my $dispatcher = sub {
        return unless -f;
        return unless /\.html$/;
        $LX->parse($_);
        for ( @{ $LX->links() } ) {
            my $href = $_->{href};
            next
              unless $href =~ /^file:/;    # Links to other packages are file://
            $href =~ s@^file://@@;
            $href =~ s!#[t|v]:.*$!!;
            push @files, $href;
        }
    };
    File::Find::find( $dispatcher, $root );
    my @files = uniq(@files);

    if ( !@files ) {
        return ();
    }

    return uniq( sort( map { /^(.*):.*$/; $1 } `dpkg -S @files` ) );
}

sub substitute_depends {
    my ($config) = @_;
    my $dependencies = $config->[0]->{'depends'};
    my @dependencies = split /\s+/, $dependencies;
    @dependencies = grep $_, @dependencies;    # drop empty strings

    for_binpkg qw(dev prof), sub {
        my $mk_value = sub { return cabal_id_to_virtual_package $type, @_; };
        my @values = map { $mk_value->($_) } @dependencies;

        # Make -prof package depend on -dev one.
        if ( $type eq 'prof' ) {
            my $devpkg = binpkg 'dev';
            unshift @values, "$devpkg (= \${binary:Version})";
        }

        my $value = join( ',', @values );
        addsubstvar( $binpkg, 'haskell:Depends', $value );
    };

    my @doc_deps = doc_package_dependencies;
    my $docpkg   = binpkg 'doc';

    addsubstvar( $docpkg, 'haskell:Recommends', join( ',', @doc_deps ) );

    for_binpkg qw(doc), sub {
        my $version = `haddock --interface-version` or die $!;
        addsubstvar( $binpkg, "haskell:Depends", "haddock-interface-$version" );
    };

    for_binpkg qw(dev), sub {
        my @suggests = for_binpkg qw(doc prof), sub { return $binpkg; };
        my $suggests = join( ',', @suggests );
        addsubstvar( $binpkg, 'haskell:Suggests', $suggests );
    };
    if ( defined exec_binpkg ) {
        addsubstvar( exec_binpkg, 'misc:Depends', 'libgmp10' );
    }
}

=pod

Since in most cases description of binary packages differs slightly,
it is written once in source package I<X-Description> field and
automatically substituted into binary packages. This transformation
performed in C<substitute_description>.

=cut

sub substitute_description {
    my $x_description =
      new Parse::DebControl->parse_file("./debian/control")->[0]
      ->{'X-Description'};
    my %short_blurb = (
        dev  => '',
        prof => '; profiling libraries',
        doc  => '; documentation'
    );
    my %blurb = (
        dev =>
'This package provides a library for the Haskell programming language.${Newline}',
        prof =>
'This package provides a library for the Haskell programming language, compiled${Newline}for profiling.  ',
        doc =>
'This package provides the documentation for a library for the Haskell${Newline}programming language.${Newline}'
    );
    $blurb{$_} .=
      'See http://www.haskell.org/ for more information on Haskell.'
      for keys %blurb;
    my ( $short_desc, @long_desc ) = split( '\n', $x_description );
    my $long_desc = join( '${Newline}', @long_desc );

    for_binpkg sub {
        addsubstvar( $binpkg, 'haskell:Blurb',            $blurb{$type} );
        addsubstvar( $binpkg, 'haskell:ShortBlurb',       $short_blurb{$type} );
        addsubstvar( $binpkg, 'haskell:ShortDescription', $short_desc );
        addsubstvar( $binpkg, 'haskell:LongDescription',  $long_desc );
    };
}

sub add_lintian_overrides {
    for_binpkg qw(dev), sub {
        my $dev_override = "debian/$binpkg.lintian-overrides";
        if ( -f $dev_override ) {
            print_and_doit( "cp", "-f", $dev_override, "$dev_override.bak" );
        }
        open( my $fd, ">>$dev_override" );
        print $fd "binary-or-shlib-defines-rpath\n";
        close($fd);
        print_and_doit( "cat", $dev_override );
    };
}

sub manual_dh_shlibdeps {
    my ($config) = @_;
    my $probefile = "debian/probe";
    my @cc_flags;

    push @cc_flags, "-l$_"
      for ( split /\s+/, $config->[0]->{'extra-libraries'} );
    open( my $fd, ">>$probefile.c" );
    print $fd "int main(void) { return 0; }";
    print_and_doit( 'cc', @cc_flags, "$probefile.c", "-o", $probefile );

    for_binpkg qw(dev), sub {
        print_and_doit( 'dpkg-shlibdeps', '--warnings=1',
            "-Tdebian/$binpkg.substvars", $probefile );
    };
    print_and_doit( 'rm', '-f', "$probefile.c", $probefile );
}

sub install {
    run_setup( 'copy', "--destdir=$TMP_INSTALL_DIR" );
    do_install( do_dispatch() );
    my $pkg_config = install_pkg_config;
    if ( defined $pkg_config ) {
        my $config = new Parse::DebControl->parse_file($pkg_config);
        substitute_provides $config;
        substitute_depends $config;
        if (USE_LEGACY) {
            manual_dh_shlibdeps $config;
        }
    }

    substitute_description;
    add_lintian_overrides;
}

sub clean {
    print_and_doit( 'rm', '-f', 'Setup.hi', 'Setup.o' );
    print_and_doit( 'rm', '-f', <*.conf> );
    print_and_doit( 'rm', '-f', $SETUP_BIN_NAME );
    print_and_doit( 'rm', '-fr', $TMP_INSTALL_DIR );
    print_and_doit( 'rm', '-fr', $HC_BUILD_DIR );
    for my $binpkg ( @{ $dh{DOPACKAGES} } ) {
        print_and_doit( 'rm', '-fr', "debian/$binpkg" );
    }
    for_binpkg qw(dev), sub {
        my $dev_override = "debian/$binpkg.lintian-overrides";
        if ( -f "$dev_override.bak" ) {
            print_and_doit( "mv", "-f", "$dev_override.bak", "$dev_override" );
        }
        else {
            print_and_doit( "rm", "-f", "$dev_override" );
        }
    };
}

1;

# vim: sw=4:et
