#!/usr/bin/perl

=pod

Build System Overview

 - Makefile.PL calls GHC to compile Setup.hs to ./Setup.exe.
 - Makefile.PL writes a Makefile that will call util/build_pugs.pl
   upon "make".
 - build_pugs.pl writes a Pugs.cabal file based on the flags given in
   the Makefile.
 - build_pugs.pl calls ./Setup.exe.
 - Setup.exe builds dist/build/libHSPugs-6.XXX.YYY.a and returns to
   build_pugs.pl.
 - build_pugs.pl calls GHC to build ./pugs.exe
   (by simply linking the freshly built libHSPugs package with src/Main.hs).

=cut

use 5.006;
use strict;
use warnings;
use Config;
use Cwd qw(abs_path cwd);
use File::Spec;
use File::Copy;
use File::Basename;
use FindBin;
use ExtUtils::Embed;
BEGIN { chdir $FindBin::RealBin };
use inc::Module::Install;
use lib 'inc';
use PugsBuild::Config;
use Carp;

# Win32/CygWin GHC appears to be more memory hungry than its
# *nix counterpart, causing it to bail part-way through builds.
# This value will be used only if 'ghc_heap_size' in config.yml
# is not defined.

use constant WIN32_GHC_HEAPSIZE => "348m";

# Special case for migration - remove old version of Syck
unlink 'src/Data/Yaml/Syck.hs' if -e 'src/Data/Yaml/Syck.hs';

my @warn;
my $pugs = "pugs$Config{_exe}";
my ($fs) = ($Config{sitelib} =~ /([\/\\])/)
       or die "Can't determine file_sep";
my $thispugs = ".${fs}pugs$Config{_exe}";
my $version_h = "src/Pugs/pugs_version.h";
my $config_h = "src/Pugs/pugs_config.h";
my @srcdirs  = grep {-d} glob("src"), glob("src/*"), glob("src/*/*"), glob("src/*/*/*");
my @hsfiles  = map {glob "$_/*.hs"} @srcdirs;
push @hsfiles, qw<src/Pugs/Config.hs src/Pugs/CodeGen/PIR/Prelude.hs>;
my @hppfiles = map {my $x=$_; $x=~s/\.hs$/.hpp/; $x} @hsfiles;

warn_cygwin     ();

print "*** Probing configuration (please ignore any warnings)...\n\n";

name            ('Perl6-Pugs');
version_from    ('lib/Perl6/Pugs.pm');
abstract_from   ('lib/Perl6/Pugs.pm');
author          ('Audrey Tang <cpan@audreyt.org>');
license         ('perl');
install_script  ($pugs);
install_script  (glob('script/*'));
install_script  ('util/prove6');
recommends      ('Perl6::Bible');
recommends      ('Inline');
recommends      ('Filter::Simple');
recommends      ('LWP::Simple');
recommends      ('LWP');
build_requires  ('ExtUtils::MakeMaker' => 6.15);
build_requires  ('FindBin');
build_requires  ('File::Path');
include         ('Module::Install::Makefile::Name');
include         ('Module::Install::Makefile::Version');
build_subdirs   (map fixpaths($_), grep {
                   -f "$_/Makefile.PL" && not -l "$_/Makefile.PL"
                 } glob("ext/*"), glob("docs/*")
                );

my $version = version();
$version .= 0 until length($version) >= length('0.123456');
$version =~ s{6\.(\d{3})(\d{3})?}{join '.', 6, int($1), int($2||0)}e;
version($version);

makemaker_args  (
    test => { TESTS => join ' ', "t/*/*.t", "t/*/*/*.t" }, # , "perl5/*/t/*.t" },
    MAN1PODS => {},
);

if (my $prefix = PugsBuild::Config->lookup('install_dir')) {
    makemaker_args(PREFIX => $prefix);
}

clean_files     (map fixpaths($_),
    $version_h, $config_h,
    (map {"$_/*.hpp"} @srcdirs),
    "pugs$Config{_exe}", "pil$Config{_exe}",
    "src/gen_prelude$Config{_exe}",
    "Setup$Config{_exe}", "util/ghc-pkg-wrapper$Config{_exe}",
    "util/runcompiler$Config{_exe}", qw(
    src/Pugs/pugs_config.h src/Pugs/Config.hs blib6 dist
    src/Pugs/Prelude.hs src/Pugs/CodeGen/PIR/Prelude.hs test.log
    src/Pugs/Embed/Parrot.hs src/Pugs/Embed/Parrot_hsc.*
    src/Pugs/*/*_stub.*
    util/ghc-pkg-wrapper.hs util/ghc-pkg-wrapper.hi util/ghc-pkg-wrapper.o
    util/runcompiler.hs util/runcompiler.hi util/runcompiler.o
    temp-ex* unlink-test* Prelude Pugs.cabal Setup.hi Setup.o
    .setup-config .installed-pkg-config
    temp-test.*.*-out tempfile.*.* create_this_file create_this_file2
    third-party/*/*.hi third-party/*/*.o
    third-party/*/dist
    ),
    object_files("src/Pugs", "src/Pugs/*", "src/Pugs/*/*"),
);

set_postamble   ();
no_index        (
    directory =>
        qw< inc debian modules perl5 ext script util docs examples src >
);
sign            (1);
WritePugs       (5);

print(('=' x 78), "\n\n");

print @warn;

print << ".";
*** Enter '$Config{make}' to build Pugs.  If compilation is too slow,
    consider using '$Config{make} fast' instead.

.

################################################################################
sub object_files  {
    map { ("$_/*.o", "$_/*.hi") } @_;
}

sub set_postamble {
    my @srcfiles = map { glob("$_/*.*hs") } @srcdirs;
    push @srcfiles, map { glob("$_/*.*hs-boot") } @srcdirs;
    push @srcfiles, map { map { substr($_, 0, -1) } glob("$_/*.*hsc") } @srcdirs;

    my ($ghc, $ghc_version, $ghc_flags, $ghc_pkg) = assert_ghc();
    my $hsc2hs = $ENV{HSC2HS};
    my $setup = File::Spec->catfile(".", "Setup$Config{_exe}");
    my $ghc_wrapper = File::Spec->catfile(".", "util", "runcompiler$Config{_exe}");
    my $ghc_pkg_wrapper = File::Spec->catfile(".", "util", "ghc-pkg-wrapper$Config{_exe}");
    my $config_path = File::Spec->catfile(cwd(), 'third-party', 'installed', 'packages.conf');

    if (!$hsc2hs) {
        $hsc2hs = $ghc;
        $hsc2hs =~ s{(.*)ghc}{$1hsc2hs};
    }

    preprocess(
            File::Spec->catfile('util', 'runcompiler.hs.in'),
            File::Spec->catfile('util', 'runcompiler.hs'),
            CONFIG_PATH => $config_path,
            GHC => $ghc,
            GHC_PKG => $ghc_pkg,
    );
    preprocess(
            File::Spec->catfile('util', 'ghc-pkg-wrapper.hs.in'),
            File::Spec->catfile('util', 'ghc-pkg-wrapper.hs'),
            CONFIG_PATH => $config_path,
            GHC => $ghc,
            GHC_PKG => $ghc_pkg,
    );

    # set up install path for third-party modules
    mkdir File::Spec->catdir('third-party', 'installed');

    compile_hs($ghc, 'Setup', $setup);
    compile_hs($ghc, File::Spec->catfile('util', 'ghc-pkg-wrapper'), $ghc_pkg_wrapper);
    compile_hs($ghc, File::Spec->catfile('util', 'runcompiler'), $ghc_wrapper);

    # Add GHC to PATH
    local $ENV{PATH} = dirname($ghc) . $Config{path_sep} . $ENV{PATH};

    if (not try_compile
        ( "import Control.Monad.RWS\n"
        . "main = let (x, (), ()) = runRWS (return ()) () () in return x\n"
        , $ghc, qw(-hide-all-packages -package mtl -package base)
        ),
    ) {
        # We are missing mtl somehow, likely because GHC 6.6's extra pkg is not there
        die <<'EOD';
*** Could not load the "mtl" package in your GHC installation.

    This is likely because the "extralibs" of GHC 6.6 was not built along
    with the base GHC distribution.

    To install extra libraries, extract both "-src" and "-src-extralibs"
    distributions from http://haskell.org/ghc/download_ghc_66.html in the
    same directory, then cd into ghc-6.6/ and build GHC from there.
EOD
    }

    my $native_fps;

    if (try_compile("import Data.ByteString (useAsCStringLen)\n"
            ."main :: IO ()\n"
            ."main = main\n", $ghc, qw(-hide-all-packages -package base))) {
        # We have native ByteString; dummy the fps out
        $native_fps = 1;
        copy "third-party/fps/fps.cabal.dummy" => "third-party/fps/pugs-fps.cabal";
        unlink "third-party/fps/fps.cabal"; # legacy
    } else {
        # Use bundled ByteString - unregister user-fps installed by earlier Pugs
        system($ghc_pkg, qw( --user unregister fps ));
        copy "third-party/fps/fps.cabal.in" => "third-party/fps/pugs-fps.cabal";
        unlink "third-party/fps/fps.cabal"; # legacy
    }

    $ghc = $ghc_wrapper;
    $ghc_pkg = $ghc_pkg_wrapper;

    my $heap;

    # Win32 GHC is a memory hog.  We need to provide it with a memory sty.
    # We still use the option from config.yml if available.

    # XXX - This is no longer needed for GHC 6.6!
    if ($Config{osname} eq 'cygwin' or $Config{osname} eq 'MSWin32') {
        $heap = WIN32_GHC_HEAPSIZE;
    }

    $heap = PugsBuild::Config->lookup('ghc_heap_size') || $heap;
    $ghc_flags .= " +RTS -M$heap -RTS" if $heap;
    # $ghc_flags .= ' -dcore-lint';
    # $ghc_flags .= " -keep-tmp-files";

    if ($ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /\bhaskell\b/i) {
        if (has_ghc_package('plugins')
            and  try_compile("import System.Eval\n"
                            ."main :: IO ()\n"
                            .'main = (eval_ "return ()" [] [] [] [] :: IO (Either [String] (Maybe ()))) >> return ()', $ghc, '-package', 'plugins')) {
            $ghc_flags .= ' -package plugins -DPUGS_HAVE_HSPLUGINS ';
        }
        else {
            push @warn, << '.';
*** Inline Haskell support disabled.  If you want dynamic loading
    of haskell modules, please install the hs-plugins library:
        http://www.cse.unsw.edu.au/~dons/hs-plugins/
    Remember to "make register" after "make install" for hs-plugins!

.
        }
    }

    if (has_ghc_package('readline') 
        and  try_compile("import System.Console.Readline\n"
                        ."main :: IO ()\n"
                        ."main = do\n"
                        ."  setCatchSignals False\n"
                        ."  setCatchSigwinch False\n"
                        .'  _ <- readline "" '."\n"
                        .'  return ()', $ghc)) {
      $ghc_flags .= ' -DPUGS_HAVE_READLINE '; # -package readline';
    }
    else {
      push @warn, << '.';
*** Readline support disabled.  If you want readline support,
    please install the GNU readline library.

.
    }

    my $ghc_output = ''; # "-o pugs$Config{_exe} src/Main.hs";
    my $hasktags = $ENV{HASKTAGS} || 'hasktags';

    # map a bunch of .c files to a bunch of expected .o files
    my $o = sub { map { substr($_, 0, -1) . 'o' } @_ };

    my $pcre_c    = "src/pcre/pcre.c";
    my ($pcre)    = $o->($pcre_c);

    my @prereqs = ($pcre);
    my @derived_srcfiles = qw< src/Pugs/Embed/Parrot.hs src/Pugs/pugs_config.h src/Pugs/pugs_version.h src/Pugs/Config.hs src/Pugs/Prelude.hs src/Pugs/CodeGen/PIR/Prelude.hs >;

    my $embed_flags = "";
    my $hsc2hs_flags = "";
    my $ccdlflags = "";

    # [-!]perl5 does not work with the \bFLAG\b convention used in this file.
    # It should be removed, or the convention, and code, fixed.
    # Went in 2006-08-30.  Depreciated 2006-09-11.
    if ($ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /[-!]perl5\b/i) {
        push @warn,  << '.';
*** PUGS_EMBED flags "-perl5" and "!perl5" are depreciated and buggy.
    Please use "noperl5" instead.

.
    }
    if (!$ENV{PUGS_EMBED} or $ENV{PUGS_EMBED} !~ /(?:[-!]|\bno)perl5\b/i) {
        $ENV{PUGS_EMBED} .= " perl5 ";
        #push @prereqs, "src/perl5/p5embed.o"; # XXX
        # $ghc_output .= " src/perl5/p5embed.o ";
        $embed_flags .= " -DPUGS_HAVE_PERL5 -isrc/perl5 ";
        my $flags = "$Config{ccflags} $Config{ccdlflags} ";
        if ($flags =~ /\S/) {
            $flags =~ s{([\\"'])}{\\$1}g;
            my @flags = grep { length $_ } split /\s+/, $flags;
            if ($^O eq 'MSWin32') {
                if ($Config{libperl} =~ /lib(\w+)\.a/) {
                    $embed_flags .= " -optl-l$1 ";
                }
                elsif (defined &Win32::BuildNumber) {
                    # We are on ActivePerl -- Kluge massively!

                    no warnings 'once';
                    our %MY_CONFIG = %Config;
                    *Config = *MY_CONFIG;
                    *Config::Config = *MY_CONFIG;
                    *ExtUtils::MM_Win32::Config = *MY_CONFIG;
                    *ExtUtils::MM_Unix::Config = *MY_CONFIG;

                    $Config{ccflags} =~ s/-libpath:"?(.*?)"? //g;
                    $Config{ccdlflags} =~ s/-libpath:"?(.*?)"? //g;
                    $Config{lddlflags} =~ s/-libpath:"?(.*?)"? //g;
                    $Config{ldflags} =~ s/-libpath:"?(.*?)"? //g or die "ldflags: $Config{ldflags} does not contain -libpath:";

                    my $lib = "$1/$Config{libperl}";
                    $embed_flags .= " -optl\"$lib\" ";

                    $flags = "$Config{ccflags} $Config{ccdlflags}";
                    $flags =~ s{([\\"'])}{\\$1}g;
                    @flags = grep { length $_ } split /\s+/, $flags;
                }
                else {
                    warn "Unrecognized libperl shared library: $Config{libperl}, proceeding anyway...\n";
                }

                $ccdlflags .= (/^-[DIL]/ ? ' -optc' : ' -optl') . qq["$_" ] for @flags;
                $embed_flags .= " -optc-Ddirent=DIRENT";
            }
            else {
                $embed_flags .= " -optc$_" for grep length, split(/\s+/, ccopts());
                $embed_flags .= " -optl$_" for grep length, split(/\s+/, ldopts());

            }

            $embed_flags .= " $_" for grep { /-[DIL]/ } split(/\s+/, ccopts());
            $embed_flags .= " $_" for grep { /-[DIL]/ } split(/\s+/, ldopts());

            if ($Config{osname} eq 'cygwin') {
                my $cygpath = sub {
                    my $path = `cygpath @_`;
                    chomp $path;
                    $path =~ s{\\}{/}g;
                    return $path;
                };
                $embed_flags =~ s{(/usr/\S+)}{$cygpath->($1)}eg;
                $embed_flags =~ s{/cygdrive/(\w)/}{$1:/}g;
                #warn "** Cygwin embedding flags: embed_flags\n";
            }
        }
    }
    else {
        push @warn, << '.';
*** Perl 5 embedding disabled.  If you want Perl 5 support, please set the
    PUGS_EMBED environment variable to not contain "noperl5".

.
    }
    
    if ($ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /\bparrot\b/i and $Config{cc} eq 'cl') {
        push @warn, << '.';
*** Parrot linking not supported with MSVC.  Parrot linking will be disabled.

.
        $ENV{PUGS_EMBED} =~ s/\bparrot\b//g;
    }

    # Respect CC setting
    my $cc = $ENV{CC} || 'gcc';
    $ghc_flags .= " -pgmc $cc " unless $cc eq 'gcc';

    if ($Config{cf_by} eq 'Debian Project' and $ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /\bperl5\b/i) {
        # Is it safe to remove 'Debian Project' above, to test on all platforms?
        my $need_path = 1;
        my $libperlpath = '';
        my $libperl = $Config{libperl};
        foreach my $path (split(/\s/, $Config{libpth} . ' ' . $Config{libsdirs})) {
            if (-e "$path/libperl.so") {
                $need_path = 0;
                last;
            } elsif (-e "$path/$libperl") {
                $libperlpath ||= $path;
            }
        }
        if ($need_path) {
            my $message = '';
            $message .= qq[        * Symlink $libperlpath/$libperl to libperl.so\n] if ($libperlpath and $libperl);
            $message .= qq[        * Install libperl-dev package\n] if ($Config{cf_by} eq 'Debian Project');
            die <<EOD;
*** Could not find libperl.so in: $Config{libpth} $Config{libsdirs}
    Solutions include:
        * Add noperl5 to PUGS_EMBED
$message
EOD
        }
    }

    if ($ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /\bparrot\b/i) {
        my $base = $ENV{PARROT_PATH};
        my $parrot_config = which('parrot-config.imc') || which('parrot-config.pir') || which('parrot-config') || '';
        if (!$base && -e $parrot_config) {
            $base = (File::Spec->splitpath($parrot_config))[1];
        }
        if (!$base) {
            $parrot_config = which('parrot-config') || '';
            $base = (File::Spec->splitpath($parrot_config))[1] if -e $parrot_config;
        }
        if (!$base and -d File::Spec->catdir(File::Spec->updir, 'parrot')) {
            $base = abs_path(File::Spec->catdir(File::Spec->updir, 'parrot'));
        }
        if (!$base and -d File::Spec->catdir(File::Spec->updir, 'parrot-trunk')) {
            $base = abs_path(File::Spec->catdir(File::Spec->updir, 'parrot-trunk'));
        }

        my $temp = File::Spec->catfile($base, 'parrot-config.imc');
        if (!$parrot_config && -e $temp) {
          $parrot_config = $temp;
        }
        
        $temp = File::Spec->catfile($base, 'parrot-config.pir');
        if (!$parrot_config && -e $temp) {
          $parrot_config = $temp;
        }
        
        $temp = File::Spec->catfile($base, 'parrot-config');
        if (!$parrot_config && -e $temp) {
          $parrot_config = $temp;
        }

        $base =~ s/bin\/*$//;
        
        (-d $base and -e $parrot_config)
            or die "*** Please set \$ENV{PARROT_PATH} to the base path with a built parrot tree.\n";
        my $ldflags = parrot_config($base, $parrot_config, 'ldflags');
        my $libs = parrot_config($base, $parrot_config, 'libs');
        my $icuflags = parrot_config($base, $parrot_config, 'icu_shared');
        my $include_path = parrot_config($base, $parrot_config, 'prefix') . parrot_config($base, $parrot_config, 'inc');
        my $rpath_blib = parrot_config($base, $parrot_config, 'rpath_blib');
        my $build_dir = parrot_config($base, $parrot_config, 'top_builddir', 'build_dir');
        my $is_shared = parrot_config($base, $parrot_config, 'parrot_is_shared');
        my $parrot_libdir = parrot_config($base, $parrot_config, 'lib_dir');

        # Convert flags to -optc-*,etc, to pass through ghc. c/ompiler l/inker
        $ldflags =~ s/(^|\s)-/$1-optl-/g;
        $libs =~ s/(^|\s)-/$1-optl-/g;
        $icuflags =~ s/(^|\s)-/$1-optl-/g;
        $include_path =~ s/(^|\s)-/$1-optc-/g;
        $embed_flags .= " -I$include_path" if $include_path =~ /\S/;
        $rpath_blib =~ s/(^|\s)-/$1-optl-/g;

        $embed_flags .= " -I$base/include -L$base/lib -L$base/blib/lib -DPUGS_HAVE_PARROT -L/usr/local/lib $ldflags ";
        $embed_flags .= " -lparrot $libs $icuflags ";
        my $config = $parrot_libdir."/parrot/config/install_config$Config{_o}";
        $config = $parrot_libdir."/parrot/config/parrot_config$Config{_o}" unless -e $config;
        $config = $parrot_libdir."/parrot/config/null_config$Config{_o}" unless -e $config;
        $config = "$build_dir/src/install_config$Config{_o}" unless -e $config;
        $config = "$build_dir/src/parrot_config$Config{_o}" unless -e $config;
        $config = "$build_dir/src/null_config$Config{_o}" unless -e $config;

        die <<"EOD" unless -e $config or $is_shared; 
*** Could not find src/null_config.o in $build_dir.
    Solutions include:
        * Remove parrot from PUGS_EMBED
        * Place a built Parrot source tree under $build_dir.
EOD

        $embed_flags .= " $config ";
        $embed_flags .= " $rpath_blib " if -d $build_dir;

        # parrot include paths for hsc2hs
        $hsc2hs_flags .= " -DPUGS_HAVE_PARROT -I$base/include ";

        push @warn, << ".";
*** Embedded 'parrot' enabled.  You can use it for Perl 6 regex support by
    setting the PUGS_REGEX_ENGINE environment variable to "PGE" at runtime.
.
    }
    else {
        my $whichparrot = can_run('parrot');

        if ($whichparrot) {
            push @warn, << ".";
*** External 'parrot' executable found in your PATH at:

        $whichparrot

    You can use it for Perl 6 regex support by setting the
    PUGS_REGEX_ENGINE environment variable to "PGE" at runtime.

    If you want to link against Parrot, set the PUGS_EMBED environment
    variable to contain 'parrot', the PARROT_PATH environment variable to
    the path of a built parrot tree, then run Makefile.PL again.

.
        }
    }

    my $config = get_pugs_config();
    my $is_win32 = ($^O =~ /MSWin|mingw|cygwin/i);
    my $threaded = (try_compile_and_run("main :: IO ()\nmain = return ()", $ghc, "-threaded")) ? '-threaded' : '';

    if ($threaded and $ENV{PUGS_EMBED} and $ENV{PUGS_EMBED} =~ /\bperl5\b/ and !$Config{usethreads}) {
        push @warn, << '.';
*** Thread support disabled because you are building with embedded perl5 and
    your perl5 is not threaded.

.

        $threaded = '';
    }
    if ($threaded && $ENV{PUGS_NO_THREADS}) {
        push @warn, << '.';
*** Thread support disabled due to explicit request in PUGS_NO_THREADS.

.
        $threaded = '';
    }

    # As of now, Test::TAP::HTMLMatrix is the key dependency for smokes,
    # so we need only check for that.
    eval { require Test::TAP::HTMLMatrix } or push @warn, << '.';
*** You do not appear to have a Smoke Kit installed. You can still build
    Pugs and even run `make test', but you won't be able to run the more
    modern `make smoke' target to produce nice graphs and send them to the
    public smoke server. Installing Task::Smoke from CPAN will bring in
    the necessary dependencies.

.
    # XXX - hsplugins doesn't build with profiles
    my $profiled_flags = $ghc_flags;
    $profiled_flags =~ s{-DPUGS_HAVE_HSPLUGINS}{};
    $profiled_flags =~ s{-package plugins}{};

    my $emit = sub {
        my $o = shift;
        my $c = substr($o, 0, -1) . 'c';
        return "$o : $c\n\t$ghc $threaded $embed_flags $ghc_flags -no-link -no-hs-main -O -o $o $c\n";
    };

    mkdir 'dist';
    mkdir 'dist/build';
    mkdir 'dist/build/src';

    # Using the Win32 Haskell compiler results in a setup program that
    # doesn't grok cygwin paths.  This kludge takes any /cygdrive/
    # style paths and rewrites them.  This really should be factored
    # out to a common module.  Better still, we should make a Cygwin-native
    # GHC.

    if ($Config{osname} eq 'cygwin') {

        # NB.  We're exploiting for's aliasing of variables.
        foreach my $path ($ghc_pkg, $hsc2hs, $ghc) {
            $path =~ s{^/cygdrive/(\w)/}{$1:/};
        }
    }

    my $setup_flags = "--prefix=\$(DESTDIR) --with-hc-pkg=$ghc_pkg --with-hsc2hs=$hsc2hs --ghc --with-compiler=$ghc";

    my $svn_entries = '';

    if (-d '.svn') {
        $svn_entries = File::Spec->catfile('.svn', 'entries');
    }
    #$svn_entries = 'force_run';

    # logic for Judy:

=begin NOJUDY

    if ($Config{osname} ne 'MSWin32') {
        warn "Configuring Judy...\n";
        chdir "third-party/judy/Judy-1.0.3";
        copy('src/Judy.h', '../../HsJudy');
        system("./configure") unless -e "config.status" and -e "Makefile";
        chdir "../../..";
    }

    my $judyclean;
    if ($Config{osname} eq 'MSWin32') {
        $judyclean = 'cd third-party\judy\Judy-1.0.3\src && '.
            'nmake /nologo /F Makefile.win32 clean';
    } else {
        my $make = $Config{make};

        # Judy at this moment wants GNU make.
        $make = 'gmake' unless `$make --version` =~ /GNU/;

        $judyclean = "cd third-party/judy/Judy-1.0.3 \&\& $make clean \&\& perl cleanmore.pl";
    }

=end NOJUDY
=cut

    postamble(fixpaths(<< "."));
$config_h : lib/Perl6/Pugs.pm util/config_h.pl
	\$(PERL) util/config_h.pl "$ghc $ghc_flags"

$version_h :
	\$(PERL) util/version_h.pl $version_h

@{[join("\n", map {$emit->($_)} grep { /\.o$/ } @prereqs)]}

src/Pugs/Config.hs : util/PugsConfig.pm
	\$(PERL) -Iutil -MPugsConfig -e "PugsConfig->write_config_module" > src/Pugs/Config.hs

src/gen_prelude$Config{_exe} : src/gen_prelude.hs
	$ghc -O0 --make -o src/gen_prelude$Config{_exe} src/gen_prelude.hs

src/Pugs/CodeGen/PIR/Prelude.hs : src/gen_prelude$Config{_exe} src/perl6/Prelude/PIR.pm
	\@\$(PERL) -e "mkdir q-src/Pugs/CodeGen/PIR-"
	src/gen_prelude$Config{_exe} Pugs.CodeGen.PIR.Prelude < src/perl6/Prelude/PIR.pm > src/Pugs/CodeGen/PIR/Prelude.hs

src/Pugs/Prelude.hs : src/perl6/Prelude.pm util/gen_prelude.pl
	\$(PERL) util/gen_prelude.pl -v --touch --inline -i src/perl6/Prelude.pm --output src/Pugs/Prelude.hs

${() = '%.hpp : %.hs @prereqs $version_h
	$ghc $threaded $ghc_flags -DHADDOCK -E \$< -o \$@
	\$(PERL) util/munge_haddock.pl \$@'; \''}

.SUFFIXES: .hs .hpp

.hs.hpp :
	$ghc $threaded $ghc_flags -DHADDOCK -E \$< -o \$@
	\$(PERL) util/munge_haddock.pl \$@

.SUFFIXES: .hsc .hs

.hsc.hs :
	\$(PERL) -MFile::Spec -e "sub p () { File::Spec->splitpath(ARGV->[0]) }; chdir((p)[1]); system(q($hsc2hs), qw($hsc2hs_flags), (p)[2]);" \$<

.SUFFIXES: .grammar .hs

.grammar.hs :
	\$(PERL) util/file_to_hs.pl \$< \$@

.SUFFIXES: .pil .hs

.pil.hs :
	\$(PERL) util/file_to_hs.pl \$< \$@

src/Emit/PIR.hs:
src/Pugs/AST/Internals.hs:
src/Pugs/PIL1.hs:
src/Pugs/PIL2.hs:
util/drift.pl:

src/Emit/PIR/Instances.hs: src/Emit/PIR.hs util/drift.pl
	\$(PERL) util/drift.pl src/Emit/PIR.hs

src/Pugs/AST/Internals/Instances.hs: src/Pugs/AST/Internals.hs util/drift.pl
	\$(PERL) util/drift.pl src/Pugs/AST/Internals.hs

src/Pugs/PIL1/Instances.hs: src/Pugs/PIL1.hs util/drift.pl
	\$(PERL) util/drift.pl src/Pugs/PIL1.hs

src/Pugs/PIL2/Instances.hs: src/Pugs/PIL2.hs util/drift.pl
	\$(PERL) util/drift.pl src/Pugs/PIL2.hs	

haddock : $version_h $config_h @hppfiles dist/doc/html
	haddock -t Pugs-$version -h -o dist/doc/html/ @hppfiles
	\@\$(RM_F) @{[map "$_.pre", @hppfiles]} @hppfiles
	\@\$(PERL) -le "print and print q-*** API Documentation generated in @{[File::Spec->catfile('dist', 'doc', 'html', 'index.html')]}-"

# make haddock one file at a time to get partial, unlinked output
haddock-broken : $version_h $config_h @hppfiles dist/doc/html
@{[join("\n", map {"\thaddock -t Pugs-$version -h -o dist/doc/html/ $_"} @hppfiles)]}
	\@\$(RM_F) @{[map "$_.pre", @hppfiles]} @hppfiles
	\@\$(PERL) -le "print and print q-*** Unlinked API Documentation generated in @{[File::Spec->catfile('dist', 'doc', 'html', 'index.html')]}-"

dist/doc/html :
	\@\$(PERL) -MFile::Path -e "mkpath q-dist/doc/html-"

pugs_requirements : src/Emit/PIR/Instances.hs src/Pugs/AST/Internals/Instances.hs src/Pugs/PIL1/Instances.hs src/Pugs/PIL2/Instances.hs src/Pugs/Config.hs src/Pugs/CodeGen/PIR/Prelude.hs @srcfiles $version_h $config_h config.yml src/perl6/Prelude.pm src/Pugs/Prelude.hs

prof :: profiled

fastprof :: unoptimised-profiled

unoptimized-profiled :: unoptimised-profiled

unoptimised-profiled :: pugs_requirements
	\$(PERL) util/build_pugs.pl _+SETUP $setup_flags _-SETUP _+GHC $version $ghc $ghc_pkg $ghc_version $setup --make -O0 -auto-all -prof $threaded $profiled_flags $ccdlflags $embed_flags $ghc_output _-GHC _+GEN_PRELUDE --pugs $thispugs

profiled :: pugs_requirements
	\$(PERL) util/build_pugs.pl _+SETUP $setup_flags _-SETUP _+GHC $version $ghc $ghc_pkg $ghc_version $setup --make -O -auto-all -prof $threaded $profiled_flags $ccdlflags $embed_flags $ghc_output _-GHC _+GEN_PRELUDE --pugs $thispugs

pugs.prof :: profiled
	find t -type f | grep -v D | grep -v R | grep -v pugsrun | ./pugs +RTS -p -RTS -e 'my sub exit {}; for =\$\$*IN -> \$\$t is copy { \$\$t .= chomp; require \$\$t }'

optimised :: optimized

optimized :: pugs_requirements
	\$(PERL) util/build_pugs.pl _+SETUP $setup_flags _-SETUP _+GHC $version $ghc $ghc_pkg $ghc_version $setup --make -O $threaded $ghc_flags $ccdlflags $embed_flags $ghc_output _-GHC _+GEN_PRELUDE --pugs $thispugs

fast :: unoptimized

unoptimised :: unoptimized

unoptimized :: pugs_requirements
	\$(PERL) util/build_pugs.pl _+SETUP $setup_flags _-SETUP _+GHC $version $ghc $ghc_pkg $ghc_version $setup --make -O0 $threaded $ghc_flags $ccdlflags $embed_flags $ghc_output _-GHC _+GEN_PRELUDE --pugs $thispugs

$pugs : pugs_requirements
	\$(PERL) util/build_pugs.pl _+SETUP $setup_flags _-SETUP _+GHC $version $ghc $ghc_pkg $ghc_version $setup --make __optimization__ $threaded $ghc_flags $ccdlflags $embed_flags $ghc_output _-GHC _+GEN_PRELUDE --pugs $thispugs


pirtest : test-pir

pirsmoke : smoke-pir

jssmoke : smoke-js

test :: blib6/lib/Test.pm.yml

pure_all :: blib6/lib/Test.pm.yml

test-all : test test-js test-pir test-perl5

test-pir : 
	\$(PERL) -e "ENV->{HARNESS_PERL_SWITCHES}=q+-B PIR+; system qq+$Config{make}+, q+test+"

test-js :
	\$(PERL) -e "ENV->{HARNESS_PERL_SWITCHES}=q+-B JS+; system qq+$Config{make}+, q+test+"

test-perl5 :
	\$(PERL) -e "ENV->{HARNESS_PERL_SWITCHES}=q+-B PERL5+; system qq+$Config{make}+, q+test+"

blib6/lib/Test.pm.yml :: ext/Test/lib/Test.pm
	\$(NOECHO) \$(ECHO) "Precompiling Test.pm..."
	$thispugs -CParse-YAML ext/Test/lib/Test.pm > blib6/lib/Test.pm.yml 

upload-smoke : smoke.yml
	\$(PERL) util/smokeserv/smokeserv-client.pl smoke.html smoke.yml

smoke-upload : upload-smoke

smoke.yml :
	\$(PERL) util/run-smoke.pl . smoke.html

smoke : smoke-pugs

smoke-all : smoke-pugs smoke-js smoke-pir smoke-perl5

smoke-pugs : $pugs util/run-smoke.pl all blib6/lib/Test.pm.yml
	\$(PERL) util/run-smoke.pl . smoke.html

smoke-pir : $pugs util/run-smoke.pl all
	\$(PERL) util/run-smoke.pl . smoke-pir.html -BPIR

smoke-jsperl5 : $pugs util/run-smoke.pl all
	\$(PERL) -e "ENV->{PUGS_RUNTIME}=q+JSPERL5+; exec qw+$^X util/run-smoke.pl . smoke-jsperl5.html+"

smoke-js : $pugs util/run-smoke.pl all
	\$(PERL) -e "ENV->{PUGS_RUNTIME}=q+JS+; exec qw+$^X util/run-smoke.pl . smoke-js.html+"

smoke-perl5 : $pugs util/run-smoke.pl all
	\$(PERL) -e "ENV->{PUGS_RUNTIME}=q+PERL5+; exec qw+$^X util/run-smoke.pl . smoke-perl5.html -BPERL5+"


ghci : @prereqs @derived_srcfiles
	$ghc @{[ dethread_flags($ghc_flags) ]} $ghc_output -DPUGS_UNDER_GHCI -no-link --make -O0 -fglasgow-exts -L. -idist/build -Ldist/build -idist/build/src -Ldist/build/src -isrc src/Prereqs.hs @prereqs
	\@\$(RM_RF) src/Pugs/*/*_stub.*
	$ghc @{[ dethread_flags($ghc_flags) ]} $ghc_output -DPUGS_UNDER_GHCI --interactive -fglasgow-exts -L. -idist/build -Ldist/build -idist/build/src -Ldist/build/src -isrc src/Main.hs @prereqs

ctags : @prereqs @derived_srcfiles
	echo ":ctags" | $ghc $ghc_flags $ghc_output --interactive -osuf moose -hisuf miise -fglasgow-exts -L. -idist/build -Ldist/build -idist/build/src -Ldist/build/src -isrc src/Main.hs @prereqs
        
etags : @prereqs @derived_srcfiles
	echo ":etags" | $ghc $ghc_flags $ghc_output --interactive -osuf moose -hisuf miise -fglasgow-exts -L. -idist/build -Ldist/build -idist/build/src -Ldist/build/src -isrc src/Main.hs @prereqs

pil$Config{_exe} : $config_h $pcre @srcfiles src/PIL/Native/Bootstrap.hs src/PIL/Native/Syntax.hs
	$ghc $ghc_flags --make -fglasgow-exts -H0 -isrc -Isrc -L. -fno-warn-name-shadowing -o pil$Config{_exe} -main-is PIL.main src/PIL.hs $pcre

pili : pil$Config{_exe}
	$ghc --interactive -fglasgow-exts -isrc -Isrc -L. -static -fno-warn-name-shadowing src/PIL.hs $pcre

tags : @srcfiles
	$hasktags -c @{[ grep { !/PIL/ } @srcfiles ]}
	sort tags > tags.tmp
	mv tags.tmp tags

config.yml:

src/perl6/Prelude.pm:

INST6_ARCHLIB = blib6/arch
INST6_SCRIPT = blib6/script
INST6_BIN = blib6/bin
INST6_LIB = blib6/lib
INST6_MAN1DIR = blib6/man1
INST6_MAN3DIR = blib6/man3
INSTPUGS_LIB = blib6/pugs

build_perl5 ::
@{[for_perl5("
	cd __DIR__ && perl Makefile.PL && \$(MAKE)
")]}

clean ::
	\@\$(RM_RF) third-party/installed
@{[for_perl5("
	-cd __DIR__ && \$(TEST_F) \$(FIRST_MAKEFILE) && \$(MAKE) clean
")]}

realclean ::
	\@\$(RM_RF) third-party/installed

@{[for_perl5("
	-cd __DIR__ && \$(TEST_F) \$(FIRST_MAKEFILE) && \$(MAKE) realclean
")]}

pure_all :: build_perl5
	\$(PERLRUN) util/src_to_blib.pl

register ::
	\$(PERLRUN) util/ghc_setup.pl copy --copy-prefix=\$(DESTDIR)
	\$(PERLRUN) util/ghc_setup.pl register --prefix=\$(DESTDIR)

pure_site_install ::
	\$(NOECHO) \$(MOD_INSTALL) \\
		\$(INST6_LIB) \$(DESTDIR)$config->{sitelib} \\
		\$(INST6_ARCHLIB) \$(DESTDIR)$config->{sitearch} \\
		\$(INST6_BIN) \$(DESTDIR)$config->{sitebin} \\
		\$(INST6_SCRIPT) \$(DESTDIR)$config->{sitescript} \\
		\$(INST6_MAN1DIR) \$(DESTDIR)$config->{installsiteman1dir} \\
		\$(INST6_MAN3DIR) \$(DESTDIR)$config->{installsiteman3dir} \\
		\$(INSTPUGS_LIB) \$(DESTDIR)$config->{sitelib}/auto/pugs
#	\$(PERLRUN) util/ghc_setup.pl copy --copy-prefix=\$(DESTDIR)

pure_vendor_install ::
	\$(NOECHO) \$(MOD_INSTALL) \\
		\$(INST6_LIB) \$(DESTDIR)$config->{privlib} \\
		\$(INST6_ARCHLIB) \$(DESTDIR)$config->{archlib} \\
		\$(INST6_BIN) \$(DESTDIR)$config->{installbin} \\
		\$(INST6_SCRIPT) \$(DESTDIR)$config->{installscript} \\
		\$(INST6_MAN1DIR) \$(DESTDIR)$config->{installman1dir} \\
		\$(INST6_MAN3DIR) \$(DESTDIR)$config->{installman3dir} \\
		\$(INSTPUGS_LIB) \$(DESTDIR)$config->{privlib}/auto/pugs
#	\$(PERLRUN) util/ghc_setup.pl copy --copy-prefix=\$(DESTDIR)
.
}

sub for_perl5 {
    my $cmd = shift;
    $cmd =~ s{\n}{}g;
    my @cmds;
    foreach my $dir (grep { -d } glob('perl5/*')) {
        -e "$dir/Makefile.PL" or next;

        # Skip XS modules for now
        next if glob("$dir/*.xs") or glob("$dir/*.i") or $dir =~ /-\d+/;

        my $this = $cmd;
        $this =~ s{__DIR__}{$dir}g;
        push @cmds, $this;
    }
    return join("\n", @cmds);
}

our $do_run;
sub try_compile_and_run {
    local $do_run = 1;
    try_compile(@_);
}

sub try_compile {
    my $code = shift;
    my $temp = "pugs-tmp-$$";
    my $ghc  = shift or croak "try_compile called without path to ghc";

    eval {
        open TMP, "> $temp.hs";
        print TMP $code;
        close TMP;
        system(
            $ghc, @_,
            "--make", "-v0",
            -o => "$temp.exe",
            "$temp.hs"
        );

    };

    my $ok = -s "$temp.exe";

    if ($do_run) {
        $ok = 0 unless system(abs_path("$temp.exe")) == 0;
    }

    unlink("$temp.exe");
    unlink("$temp.hs");
    unlink("$temp.hi");
    unlink("$temp.o");

    return $ok;
}

sub parrot_config {
    my $base = shift;
    my $parrot_config = shift;
    my $ac_path = abs_path();
    my $sp_base = $base;
    $sp_base =~ s{\\}{\/}g;
    chdir( $sp_base ) or die "Can't change dir to '$sp_base'";
    my $parrot = "parrot$Config{_exe}";
    $parrot = "bin/$parrot" if not -e $parrot;
    my $value;
    while (@_) {
        my $config = shift;
        $value = `./$parrot $parrot_config $config`;
        $value =~ /no such key:/ or last;
    }
    die $value if $value =~ /no such key:/;
    chomp($value);
    chdir( $ac_path ) or die "Can't change dir to '$ac_path'";
    return $value;
}

sub which {
    my $file = shift;
    for ( File::Spec->path() ) {
        my $full_name = File::Spec->catfile($_, $file);
        return $full_name if -e $full_name;
    }
    return undef;    
}

sub preprocess {
    my ($infile, $outfile, %substitutions) = @_;

    open IN, '<', $infile or die "failed to open $infile ($!)";
    open OUT, '>', $outfile or die "failed to open $outfile ($!)";

    if ($Config{osname} eq 'cygwin') {
        s{^/cygdrive/(\w)/}{$1:/} for values %substitutions;
    }

    while (<IN>) {
        while (my ($pat, $subst) = each(%substitutions)) {
            $subst =~ s{\\}{\\\\}g;
            s/__${pat}__/$subst/g
        }
        print OUT $_;
    }

    close OUT;
    close IN;
}

sub compile_hs {
    my ($ghc, $prefix, $out) = @_;

    unlink($prefix . $_) for (
        $Config{_exe},
        '.o',
        '.hi',
    );

    my $rv = system($ghc, '--make', '-o' => $out,
        (-e $prefix. '.hs') ? $prefix.'.hs' : $prefix.'.lhs'
    );
    unless (-s $out) {
        die << ".";
*** Building Setup$Config{_exe} failed (exit code $rv)
    Please check your GHC and Cabal installation.
.
    }
}
