# Copyright © 2022 Felix Lechner <felix.lechner@lease-up.com>
#
# based on a shell script library by John Goerzen
#
# 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/>.

package Debian::Debhelper::Buildsystem::Haskell::Recipes;

use v5.20;
use warnings;
use utf8;

use Exporter qw(import);

our @EXPORT_OK;

BEGIN {

    @EXPORT_OK = qw(
      run
      package_hc
      package_ext
      packages_hc
      cabal_package_ids
      cabal_depends
      hashed_dependency
      ghc_depends
      tmp_package_db
      ghc_provides
      hugs_depends
      find_config_for_ghc
      clean_recipe
      make_setup_recipe
      configure_recipe
      build_recipe
      check_recipe
      haddock_recipe
      install_recipe
    );
}

use Const::Fast;
use Cwd;
use Date::Parse qw(str2time);
use File::Basename;
use IPC::Run3;
use List::SomeUtils qw(uniq any first_value);
use Path::Tiny;
use Unicode::UTF8 qw(encode_utf8 decode_utf8);

const my $EMPTY => q{};
const my $SPACE => q{ };
const my $PLUS => q{+};
const my $SEMICOLON => q{;};
const my $LESS_THAN => q{<};
const my $DOUBLE_LESS_THAN => $LESS_THAN x 2;
const my $NEWLINE => qq{\n};

const my $WAIT_STATUS_SHIFT => 8;

const my $MINIMUM_GHC_VERSION => 8;
const my $CABAL_VERSION_IMPLYING_SIMPLE_BUILDS => 2.2;

=head1 NAME

Debian::Debhelper::Buildsystem::Haskell::Recipes -- Recipes for the Haskell build system in Debhelper

=head1 SYNOPSIS

 Debian::Debhelper::Buildsystem::Haskell::Recipes;

=head1 DESCRIPTION

A library with recipes for the Haskell build system in Debhelper.

=head1 SUBROUTINES

=over 4

=item run_quiet

=cut

sub run_quiet {
    my (@command) = @_;

    my @command_bytes = map { encode_utf8($_) } @command;

    my $stdout_bytes;
    my $stderr_bytes;
    run3(\@command_bytes, \undef, \$stdout_bytes, \$stderr_bytes);

    my $wait_status = $?;
    my $exitcode = ($wait_status >> $WAIT_STATUS_SHIFT);

    # already in UTF-8
    die encode_utf8("Non-zero exit code $exitcode.")
      . $NEWLINE
      . $stdout_bytes
      . $stderr_bytes
      if $exitcode;

    my $output = decode_utf8($stdout_bytes // $EMPTY);
    chomp $output;

    return $output;
}

=item run

=cut

sub run {
    my (@command) = @_;

    my @filtered = grep { length } @command;

    say {*STDERR} encode_utf8("Running @filtered")
      if !$ENV{DH_QUIET} || $ENV{DH_VERBOSE};

    my $output = run_quiet(@filtered);

    say {*STDERR} encode_utf8($output)
      if length $output
      && (!$ENV{DH_QUIET} || $ENV{DH_VERBOSE});

    return $output;
}

=item package_hc

=cut

sub package_hc {
    my ($installable) = @_;

    return 'ghc'
      if $installable =~ m{^ ghc (:? -prof )? $};

    my $compiler = $installable;

    # strip hackage name
    $compiler =~ s{ - .* $}{}x;

    # strip lib prefix, if any, and use result
    if ($compiler =~ s{^ lib }{}x) {
        return $compiler;
    }

    # default if there is no lib prefix
    return $ENV{DEB_DEFAULT_COMPILER};
}

=item package_ext

=cut

sub package_ext {
    my ($installable) = @_;

    return 'dev'
      if $installable eq 'ghc';

    return 'prof'
      if $installable eq 'ghc-prof';

    my $suffix = $installable;

    # strip hackage name
    $suffix =~ s{^ .* - }{}x;

    return $suffix;
}

=item packages_hc

=cut

sub packages_hc {
    my () = @_;

    # fix sort order
    local $ENV{LC_ALL} = 'C.UTF-8';

    # should be in UTF-8
    my $package_list = run('dh_listpackages');
    chomp $package_list;

    my @installables = split($SPACE, $package_list);

    my @compilers = uniq map { package_hc($_) } @installables;

    die encode_utf8(
        'Multiple compilers not supported: ' . join($SPACE, (sort @compilers)))
      if @compilers > 1;

    return $ENV{DEB_DEFAULT_COMPILER}
      if !@compilers;

    return $compilers[0];
}

=item hc_libdir

=cut

sub hc_libdir {
    my ($compiler) = @_;

    return 'usr/lib/haskell-packages/ghc/lib'
      if $compiler eq 'ghc';

    return 'usr/lib/ghcjs/.cabal/lib'
      if $compiler eq 'ghcjs';

    die encode_utf8("Don't know package_libdir for $compiler");
}

=item package_libdir

=cut

sub package_libdir {
    my ($installable) = @_;

    my $compiler = package_hc($installable);

    return hc_libdir($compiler);
}

=item hc_pkgdir

=cut

sub hc_pkgdir {
    my ($compiler) = @_;

    return 'var/lib/ghc/package.conf.d'
      if $compiler eq 'ghc';

    if ($compiler eq 'ghcjs') {

        my $cpu = run(qw{ghc -e}, 'putStr System.Info.arch');
        my $os = run(qw{ghc -e}, 'putStr System.Info.os');
        my $ghcjs_version = run(qw{ghcjs --numeric-ghcjs-version});
        my $ghcjs_ghc_version = run(qw{ghcjs --numeric-ghc-version});

        my $quadruplet = "$cpu-$os-$ghcjs_version-$ghcjs_ghc_version";

        return "usr/lib/ghcjs/.ghcjs/$quadruplet/ghcjs/package.conf.d";
    }

    die encode_utf8("Don't know pkgdir for $compiler");
}

=item package_pkgdir

=cut

sub package_pkgdir {
    my ($installable) = @_;

    my $compiler = package_hc($installable);

    return hc_pkgdir($compiler);
}

=item hc_prefix

=cut

sub hc_prefix {
    my ($compiler) = @_;

    return 'usr'
      if $compiler eq 'ghc';

    return 'usr/lib/ghcjs'
      if $compiler eq 'ghcjs';

    die encode_utf8("Don't know prefix for $compiler");
}

=item hc_haddock

=cut

sub hc_haddock {
    my ($compiler) = @_;

    return 'haddock'
      if $compiler eq 'ghc';

    return 'haddock-ghcjs'
      if $compiler eq 'ghcjs';

    die encode_utf8("Don't know haddock command for $compiler");
}

=item hc_docdir

=cut

sub hc_docdir {
    my ($compiler, $hackage_name_version) = @_;

    return "usr/lib/$compiler-doc/haddock/$hackage_name_version/";
}

=item hc_htmldir

=cut

sub hc_htmldir {
    my ($compiler, $hackage_name) = @_;

    $ENV{CABAL_PACKAGE} = $hackage_name;

    return "usr/share/doc/lib$compiler-$hackage_name-doc/html/";
}

=item dependency

=cut

sub dependency {
    my ($installable) = @_;

    my $version
      = run(qw{dpkg-query --showformat=${Version} --show}, $installable);

    my $next_upstream_version = $version;
    $next_upstream_version =~ s{ - [^-]* $}{}x;
    $next_upstream_version .= $PLUS;

    return
      "$installable (>= $version), $installable (<< $next_upstream_version)";
}

=item ghc_pkg_field

=cut

sub ghc_pkg_field {
    my ($compiler, $hackage_name, $field) = @_;

    my $output
      = run("$compiler-pkg", qw{--global field}, $hackage_name, $field);

    # may not process multi-line fields correctly
    my ($value) = split($NEWLINE, $output, 2);

    return ($value // $EMPTY);
}

=item providing_package_for_ghc

=cut

sub providing_package_for_ghc {
    my ($compiler, $type, $package_id) = @_;

    my $extension = $EMPTY;
    $extension = '_p'
      if $type eq 'prof';

    my $ghc_version= run(qw{dpkg-query --showformat=${Version} --show ghc});

    my $hackage_name = $package_id;
    $hackage_name =~ s{ - [^-]{32} $}{}x
      if system(qw{dpkg --compare-versions},
        $ghc_version,$DOUBLE_LESS_THAN, $MINIMUM_GHC_VERSION) == 0;

    my $directory_line
      = ghc_pkg_field($compiler, $hackage_name, 'library-dirs');
    my (undef, $directory_list) = split(m{ \s* : \s* }x, $directory_line, 2);
    my @library_dirs = split(m{ \s* , \s* }x, $directory_list);

    my $library_line = ghc_pkg_field($compiler, $hackage_name, 'hs-libraries');
    my (undef, $library_list) = split(m{ \s* : \s* }x, $library_line, 2);
    my @libraries = split(m{ \s* , \s* }x, $library_list);

    # look only at the first one
    my $library = $libraries[0];

    for my $directory (@library_dirs) {

        my $library_path = "$directory/lib$library$extension.a";
        next
          unless -e $library_path;

        my $line = run(qw{dpkg-query --search}, $library_path);
        my ($installable) = split(m{ \s* : \s* }x, $line, 2);

        return $installable;
    }

    return ();
}

=item cabal_package_ids

=cut

sub cabal_package_ids {
    my (@configs) = @_;

    my @ghc_pkg = tmp_package_db(@configs);

    my @package_ids;
    for my $config (@configs) {

        my $name = path($config)->basename(qr{ [.]conf $}x);
        push(@package_ids,
            run(@ghc_pkg, qw{--simple-output field}, $name, 'id'));
    }

    return @package_ids;
}

=item cabal_depends

=cut

sub cabal_depends {
    my (@configs) = @_;

    # fix sort order
    local $ENV{LC_ALL} = 'C.UTF-8';

    my @ghc_pkg = tmp_package_db(@configs);

    my @constraints;
    for my $config (@configs) {

        my $name = path($config)->basename(qr{ [.]conf $}x);
        my $depends
          = run(@ghc_pkg, qw{--simple-output field}, $name, 'depends');
        push(@constraints, split($SPACE, $depends // $EMPTY));
    }

    my @have = sort +uniq @constraints;
    my @exclude_patterns = split($SPACE, $ENV{DH_EXCLUDES} // $EMPTY);

    # not sure this complies with Debhelper expectations
    # excluded installables matching the patterns with or without version
    # the versions should probably be dropped by the caller
    s{ - [0-9] [.0-9a-zA-Z]* $}{}x for @exclude_patterns;

    my @retained;
    for my $constraint (@have) {

        next
          if any { $constraint =~ m{\Q$_\E} } @exclude_patterns;

        push(@retained, $constraint);
    }

    return @retained;
}

=item hashed_dependency

=cut

sub hashed_dependency {
    my ($compiler, $type, $package_id) = @_;

    my @ghc_pkg = usable_ghc_pkg();
    my $virtual_pkg
      = package_id_to_virtual_package($compiler, $type, $package_id, @ghc_pkg);

    # as a transition measure, check if dpkg knows about this virtual package
    return $virtual_pkg
      if system("dpkg-query --show $virtual_pkg > /dev/null 2> /dev/null")== 0;

    return ();
}

=item ghc_depends

=cut

sub ghc_depends {
    my ($compiler, $type, @configs) = @_;

    my @package_ids = cabal_depends(@configs);

    my @constraints;
    for my $package_id (@package_ids) {

        my $constraint = hashed_dependency($compiler, $type, $package_id);

        if (!length $constraint) {

            my $installable
              = providing_package_for_ghc($compiler, $type, $package_id);

            $constraint = dependency($installable);
            if (!length $constraint) {

                warn encode_utf8(
"WARNING: No Debian package provides haskell package $package_id.\n"
                );
                next;
            }
        }

        push(@constraints, $constraint);
    }

    return @constraints;
}

=item usable_ghc_pkg

=cut

sub usable_ghc_pkg {
    my () = @_;

    my @ghc_pkg;
    my $version;

    my $inplace_ghc_pkg = 'inplace/bin/ghc-pkg';
    if (-x $inplace_ghc_pkg) {

        # we are building ghc and need to use the new ghc-pkg
        @ghc_pkg = ($inplace_ghc_pkg);
        $version = run(qw{dpkg-parsechangelog --show-field Version});

    } else {
        @ghc_pkg = qw{ghc-pkg};
        $version= run(qw{dpkg-query --showformat=${Version} --show ghc});
    }

    # ghc-pkg prior to version 8 is unusable for our purposes.
    return ()
      if system(qw{dpkg --compare-versions},
        $version, $DOUBLE_LESS_THAN, $MINIMUM_GHC_VERSION)== 0;

    return @ghc_pkg;
}

=item tmp_package_db

=cut

sub tmp_package_db {
    my (@configs) = @_;

    my @ghc_pkg = usable_ghc_pkg();
    return ()
      unless @ghc_pkg;

    if (!-e 'debian/tmp-db/package.cache') {

        run(qw{mkdir debian/tmp-db});
        run(qw{cp}, @configs, 'debian/tmp-db/')
          if @configs;

        # Silence GHC 8.4's warning
        # "ignoring (possibly broken) abi-depends field for packages"
        # see also https://ghc.haskell.org/trac/ghc/ticket/14381
        run(@ghc_pkg, qw{--package-db debian/tmp-db/ recache});
    }

    return (@ghc_pkg, qw{--package-db debian/tmp-db});
}

=item ghc_provides

=cut

sub ghc_provides {
    my ($compiler, $type, @configs) = @_;

    my @ghc_pkg = tmp_package_db(@configs);
    my @package_ids = cabal_package_ids(@configs);

    my @virtual;
    push(@virtual,
        package_id_to_virtual_package($compiler, $type, $_, @ghc_pkg))
      for @package_ids;

    return @virtual;
}

=item package_id_to_virtual_package

=cut

sub package_id_to_virtual_package {
    my ($compiler, $type, $package_id, @ghc_pkg) = @_;

    my $name;
    my $version;
    my $long_abi;

    if (@ghc_pkg) {

        $name = run(@ghc_pkg, qw{--simple-output --unit-id field},
            $package_id, 'name');
        $version = run(@ghc_pkg, qw{--simple-output --unit-id field},
            $package_id, 'version');
        $long_abi = run(@ghc_pkg, qw{--simple-output --unit-id field},
            $package_id, 'abi');

    } else {

        # no usable ghc-pkg; parse package id
        my $lowercase = lc $package_id;

        ($name, $version, $long_abi)
          = ($lowercase =~ m{^ ([a-z0-9-]+) - ([0-9.]+) - (\S{32}) $}x);
    }

    # retain only the first five hex digits from abi out of 32
    my ($short_abi) = ($long_abi =~ m{^ (\S{5}) }x);

    my $virtual = lc "lib$compiler-$name-$type-$version-$short_abi";

    return $virtual;
}

=item hugs_depends

=cut

sub hugs_depends {
    my () = @_;

    my $version= run(qw{dpkg-query --showformat=${Version} --show hugs});
    my $upstream_version = $version;
    $upstream_version =~ s{ - [^-]* $}{}x;

    return ("hugs (>= $upstream_version)");
}

=item find_config_for_ghc

=cut

sub find_config_for_ghc {
    my ($installable) = @_;

    my $pkgdir = package_pkgdir($installable);

    if ($installable =~ s{ - prof $}{}x) {

        $installable .= '-dev'
          unless $installable eq 'ghc';
    }

    my $confdir = "debian/$installable/$pkgdir";

    my @configs;
    @configs = grep { $_->is_file } path($confdir)->children(qr{ [.]conf $}x)
      if -e $confdir;

    return @configs;
}

=item clean_recipe

=cut

sub clean_recipe {
    my () = @_;

    run($ENV{DEB_SETUP_BIN_NAME}, qw{clean})
      if -x $ENV{DEB_SETUP_BIN_NAME};

    run(
        qw{rm -rf dist dist-ghc dist-ghcjs dist-hugs},
        $ENV{DEB_SETUP_BIN_NAME},
        qw{Setup.hi Setup.ho Setup.o},
        glob('.*config*'));

    if (-e $ENV{DEB_LINTIAN_OVERRIDES_FILE}) {
        run(
            qw{sed -i},
            '/custom-library-search-path/ d',
            $ENV{DEB_LINTIAN_OVERRIDES_FILE});
        run('find',$ENV{DEB_LINTIAN_OVERRIDES_FILE},qw{-empty -delete});
    }

    run(qw{rm -f}, $ENV{MAKEFILE});
    run(qw{rm -rf debian/tmp-db});

    return;
}

=item make_setup_recipe

=cut

sub make_setup_recipe {
    my () = @_;

    my $shipped_setup = first_value { -e } qw{Setup.lhs Setup.hs};

    if (length $shipped_setup) {

        run(qw{ghc --make}, $shipped_setup, '-o',$ENV{DEB_SETUP_BIN_NAME});
        return;
    }

# Having a Setup.hs is considered good practice, but there are a few
# Haskell packages that don't, since cabal does not use it for build types
# other than 'Custom'. Find out the build type and use the corresponding
# standardized Setup.hs file. For more information, see
# https://www.haskell.org/cabal/users-guide/developing-packages.html#pkg-field-build-type

    my $cabal_path = "$ENV{CABAL_PACKAGE}.cabal";
    my $cabal_contents = path($cabal_path)->slurp_utf8;

    my $cabal_version= first_value { length }
    ($cabal_contents
          =~ m{^ cabal-version: (?: \s+ [<>=]+ )? \s+ (\S+) \s* $}mix);
    die encode_utf8("No cabal-version in $cabal_path")
      unless length $cabal_version;

    my $build_type= first_value { length }
    ($cabal_contents =~ m{^ build-type: \s+ (\w+) \s* }mix);

  # https://cabal.readthedocs.io/en/3.4/cabal-package.html#pkg-field-build-type
    my $default_build_type = 'Simple';
    $default_build_type = 'Custom'
      if $cabal_version < $CABAL_VERSION_IMPLYING_SIMPLE_BUILDS
      || $cabal_contents =~ m{^ custom-setup \s+ }mi;

    $build_type //= $default_build_type;

    my $stock_setup = "/usr/share/haskell-devscripts/Setup-$build_type.hs";
    die encode_utf8("Could not find a suitable Setup.hs file for $build_type")
      unless -e $stock_setup;

    run(
        qw{ghc --make}, $stock_setup, '-o',
        $ENV{DEB_SETUP_BIN_NAME},
        qw{-outputdir debian/tmp-setup-hs}
    );

    return;
}

=item configure_recipe

=cut

sub configure_recipe {
    my () = @_;

    # deleted when out of scope
    my $time_reference = Path::Tiny->tempfile;
    $time_reference->touch(str2time('1975-01-01', 'UTC'));

    # dak does not like file timestamps older than 1975
    # new tarballs from Hackage have files with mtimes at the
    # beginning of the epoch, so set old mtimes to 1998
    run(
        qw{find . ! -newer},
        $time_reference->stringify,
        qw{-exec touch -d "1998-01-01 UTC" {} ; }
    );

    my $compiler = packages_hc();

    my $package_list = run('dh_listpackages');
    chomp $package_list;

    my @installables = split($SPACE, $package_list);
    my @extensions = map { package_ext($_) } @installables;

    my $profiling;
    $profiling = '--enable-library-profiling'
      if any { $_ eq 'prof' } @extensions;

    my $ldflags_line = run(qw{dpkg-buildflags --get LDFLAGS});
    my @ldflags = split($SPACE, $ldflags_line);
    my @ghc_options = map { "--ghc-option=-optl$_" } @ldflags;

    my $pkgdir = hc_pkgdir($compiler);
    my $prefix = hc_prefix($compiler);
    my $libdir = hc_libdir($compiler);
    my $docdir
      = hc_docdir($compiler, "$ENV{CABAL_PACKAGE}-$ENV{CABAL_VERSION}");
    my $htmldir = hc_htmldir($compiler, $ENV{CABAL_PACKAGE});

    # DEB_SETUP_GHC_CONFIGURE_ARGS can contain multiple arguments
    # with their own quoting so run through shell a expansion
    my $ghc_configure_args = run_quiet(qw{sh -c echo -n},
        split($SPACE, $ENV{DEB_SETUP_GHC_CONFIGURE_ARGS} // $EMPTY));

    # the versioned form DEB_SETUP_GHC6_CONFIGURE_ARGS should perhaps be
    # abandoned in favor of the unversioned DEB_SETUP_GHC_CONFIGURE_ARGS

    run(
        $ENV{DEB_SETUP_BIN_NAME},
        'configure',
        "--$compiler",
        '-v2',
        "--package-db=/$pkgdir",
        "--prefix=/$prefix",
        "--libdir=/$libdir",
        '--libexecdir=/usr/lib',
        "--builddir=dist-$compiler",
        @ghc_options,
        "--haddockdir=/$docdir",
        "--datasubdir=$ENV{CABAL_PACKAGE}",
        "--htmldir=/$htmldir",
        $profiling,
        $ENV{NO_GHCI_FLAG},
        split($SPACE, $ENV{DEB_SETUP_GHC6_CONFIGURE_ARGS} // $EMPTY),
        split($SPACE, $ghc_configure_args // $EMPTY),
        split($SPACE, $ENV{OPTIMIZATION} // $EMPTY),
        split($SPACE, $ENV{TESTS} // $EMPTY));

    return;
}

=item build_recipe

=cut

sub build_recipe {
    my () = @_;

    my $compiler = packages_hc();
    my $output
      = run($ENV{DEB_SETUP_BIN_NAME},'build', "--builddir=dist-$compiler");

    return;
}

=item check_recipe

=cut

sub check_recipe {
    my () = @_;

    if ($ENV{DEB_ENABLE_TESTS} ne 'yes') {

        say encode_utf8(
'DEB_ENABLE_TESTS not set to yes, not running any build-time tests.'
        );
        return;
    }

    if ($ENV{DEB_BUILD_OPTIONS} =~ m{ nocheck }x) {

        say encode_utf8(
'DEB_BUILD_OPTIONS contains nocheck, not running any build-time tests.'
        );
        return;
    }

    my $compiler = packages_hc();
    my $output = run($ENV{DEB_SETUP_BIN_NAME},
        'test', "--builddir=dist-$compiler", '--show-details=direct');

    return;
}

=item haddock_recipe

=cut

sub haddock_recipe {
    my () = @_;

    my $compiler = packages_hc();
    my $haddock = hc_haddock($compiler);

    return
      unless -x "/usr/bin/$haddock";

    run(
        $ENV{DEB_SETUP_BIN_NAME},
        'haddock',
        "--builddir=dist-$compiler",
        "--with-haddock=/usr/bin/$haddock",
        "--with-ghc=$compiler",
        '--verbose=2',
        split($SPACE, $ENV{DEB_HADDOCK_OPTS} // $EMPTY));

    return;
}

=item install_recipe

=cut

sub install_recipe {
    my ($destination) = @_;

    # fix sort order
    local $ENV{LC_ALL} = 'C.UTF-8';

    my $compiler = packages_hc();

    run($ENV{DEB_SETUP_BIN_NAME},
        'copy', "--builddir=dist-$compiler", "--destdir=$destination");

    my $package_list = run('dh_listpackages');
    chomp $package_list;

    my @installables = split($SPACE, $package_list);
    for my $installable (@installables) {

        my $extension = package_ext($installable);

        if ($extension eq 'dev') {

            my $savedir = Cwd::getcwd;
            chdir($destination)
              or warn encode_utf8("Cannot change folder to $destination");

            my $libdir = package_libdir($installable);

            run(qw{mkdir --parents}, $libdir);
            run(
                'find',
                $libdir,
                qw{ ( ! -name *_p.a ! -name *.p_hi ! -type d ) },
                qw{-exec install -Dm 644},
                '{}',
                "../$installable/{}",
                $SEMICOLON
            );

            chdir($savedir)
              or warn encode_utf8("Cannot change folder to $savedir");

            my $output = run($ENV{DEB_SETUP_BIN_NAME},
                'register', "--builddir=dist-$compiler",
                qw{--gen-pkg-config --verbose=verbose+nowrap});

            if ($output
                =~ m{^Creating \s package \s registration \s file: \s+ (\S+) $}mx
            ) {

                my $pkg_config = $1;

                run(qw{sed -i}, 's/^exposed: True$/exposed: False/',
                    $pkg_config)
                  if length $ENV{HASKELL_HIDE_PACKAGES};

                my $pkgdir = package_pkgdir($installable);
                run(qw{install -Dm 644},
                    $pkg_config, "debian/$installable/$pkgdir/$pkg_config");
                run(qw{rm -f}, $pkg_config);

                if (length $ENV{DEB_GHC_EXTRA_PACKAGES}) {

                    my $EP_DIR
                      = "debian/$installable/usr/lib/haskell-packages/extra-packages";
                    run(qw{mkdir --parents}, $EP_DIR);

                    path("$EP_DIR/$ENV{CABAL_PACKAGE}-$ENV{CABAL_VERSION}")
                      ->spew_utf8($ENV{DEB_GHC_EXTRA_PACKAGES});

                } else {
                    warn encode_utf8(
'Skipping extra depends; cannot get package registration file (probably not a library).'
                    );
                }
            }
        }

        if ($extension eq 'prof') {

            my $savedir = Cwd::getcwd;
            chdir($destination)
              or warn encode_utf8("Cannot change folder to $destination");

            my $libdir = package_libdir($installable);

            run(qw{mkdir --parents}, $libdir);
            run(
                'find', $libdir,
                qw{ ( -name *_p.a -o -name *.p_hi ) },
                qw{-exec install -Dm 644},
                '{}', "../$installable/{}", $SEMICOLON
            );

            chdir($savedir)
              or warn encode_utf8("Cannot change folder to $savedir");
        }

        if ($extension eq 'doc') {

            my $savedir = Cwd::getcwd;
            chdir($destination)
              or warn encode_utf8("Cannot change folder to $destination");

            my $htmldir = hc_htmldir($compiler, $ENV{CABAL_PACKAGE});

            run(qw{mkdir --parents}, "debian/$installable/$htmldir");
            run(
                'find', "./$htmldir",
                qw{ ! -name *.haddock ! -type d},
                qw{-exec install -Dm 644},
                '{}', "../$installable/{}", $SEMICOLON
            );

            chdir($savedir)
              or warn encode_utf8("Cannot change folder to $savedir");

            my $docdir
              = hc_docdir($compiler,"$ENV{CABAL_PACKAGE}-$ENV{CABAL_VERSION}");

            my $tmp_inst_docdir = "$destination/$docdir";
            my $installable_docdir = "debian/$installable/$docdir";

            run(qw{mkdir --parents}, $installable_docdir);

            run(qw{cp --recursive}, $_->stringify, $installable_docdir)
              for path($tmp_inst_docdir)->children(qr{ [.]haddock $}x);

            if ($ENV{DEB_ENABLE_HOOGLE} eq 'yes') {

                # We cannot just invoke dh_link here because that acts on
                # either libghc-*-dev or all the binary packages, neither of
                # which is desirable (see dh_link (1)).  So we just create a
                # (policy-compliant) symlink ourselves

                my $target
                  = "debian/$installable/$htmldir/$ENV{CABAL_PACKAGE}.txt";
                if (-e $target) {

                    my $hoogle = "/usr/lib/$compiler-doc/hoogle/";
                    my $name = "debian/$installable$hoogle$installable.txt";

                    run(qw{mkdir --parents}, dirname($name));
                    run(qw{ln --symbolic --relative --no-target-directory},
                        $target, $name);
                }
            }
        }
    }

    path($ENV{DEB_LINTIAN_OVERRIDES_FILE})
      ->append_utf8('custom-library-search-path' . $NEWLINE)
      unless -e $ENV{DEB_LINTIAN_OVERRIDES_FILE}
      && path($ENV{DEB_LINTIAN_OVERRIDES_FILE})->slurp_utf8
      =~ m{^ custom-library-search-path }x;

    run('dh_haskell_provides');
    run('dh_haskell_depends');
    run('dh_haskell_shlibdeps');

    run('dh_haskell_blurbs', $compiler);

    return;
}

=head1 AUTHOR

Written by Felix Lechner <felix.lechner@lease-up.com> for Haskell Devscripts.
Based on Dh_Haskell.sh.

=head1 SEE ALSO

Debian::Debhelper::Buildsystem::haskell(3pm)

=cut

1;

# Local Variables:
# indent-tabs-mode: nil
# cperl-indent-level: 4
# End:
# vim: syntax=perl sw=4 sts=4 sr et
