6 use File
::Copy
qw(copy);
7 use File
::Path
qw(mkpath rmtree);
8 use File
::Find
qw(find);
9 use File
::Basename
qw(dirname);
10 use List
::Util
qw(max min);
15 BEGIN { chdir $FindBin::RealBin
; chdir '..'; };
17 use PugsBuild
::Config
;
19 help
() if ($ARGV[0] || '--help') =~ /^--?h(?:elp)?/i;
20 build
(classify_options
(@ARGV));
25 $0 - build a pugs executable
27 This script calls GHC to build a pugs exectuable
, optionally inlining
28 precompiled modules
in a second pass
.
30 Primary configuration settings are
read from the file
`config.yml` in
31 the build root
. You may override these settings using the PUGS_BUILD_OPTS
36 print PugsBuild
::Config
->pretty_print;
42 my $want_profiling = 0;
47 my $thispugs = { @
{ $opts->{GEN_PRELUDE
} } }->{'--pugs'} or # laugh at me now.
48 die "$0: no pugs passed in _+GEN_PRELUDE segment";
50 if ( grep '--precompile-prelude', @
{ $opts->{GEN_PRELUDE
} } ) {
51 $PugsBuild::Config
::Conf
->{'precompile_prelude'}
52 = { @
{ $opts->{GEN_PRELUDE
} } }->{'--precompile-prelude'}
55 print "Build configuration:\n" . PugsBuild
::Config
->pretty_print;
57 my ($version, $ghc, $ghc_pkg, $ghc_version, $setup, @args) = @
{$opts->{GHC
}};
59 $want_profiling = grep { /^-prof$/ } @args;
60 @args = grep { !/^-prof$/ } @args;
62 # Set heap options via environment here; Win32 needs it instead
63 # of setting on GHC flags line.
65 foreach my $arg (@args) {
67 push @rts_args, $_ if s/^\+RTS$// .. s/^-RTS$//;
69 $ENV{GHCRTS
} = join(' ', ($ENV{GHCRTS
} ?
$ENV{GHC_RTS
} : ()), @rts_args);
71 if ($Config{osname
} eq 'cygwin') {
72 my $cygwin_path = `cygpath -w /`;
75 #warn "<> processing $path...\n";
76 my $retval = `cygpath -m $path`;
78 #warn "<> Now it is $retval...\n";
82 unshift @args, '-I/usr/lib/perl5/5.8/cygwin/CORE',
83 "-optc-IC:\\ghc\\ghc-$ghc_version\\include\\mingw",
84 "-optc-ID:\\ghc\\ghc-$ghc_version\\include\\mingw",
85 '-optc-I/usr/include',
86 '-optl-I/usr/include/cygwin';
88 $arg =~ s{(-optc-[IL]|-optl|-optl-L|-I|-L)(/\S+)}{$1 . $cygpath->($2)}eg;
92 write_buildinfo
($version, $ghc, $ghc_pkg, $ghc_version, @args);
96 print "*** Building dependencies. Please wait...\n\n";
98 # Instead of --user, use our own package-conf storage position.
100 my $runcompiler = File
::Spec
->rel2abs("$pwd/util/runcompiler$Config{_exe}");
101 my $prefix = File
::Spec
->rel2abs("$pwd/third-party/installed");
102 my $hc_pkg = File
::Spec
->rel2abs("$pwd/util/ghc-pkg-wrapper$Config{_exe}");
104 if ($Config{osname
} eq 'cygwin') {
105 # NB. We're exploiting for's aliasing of variables.
106 foreach my $path ($runcompiler, $prefix, $hc_pkg) {
107 $path = `cygpath -m $path`; chomp $path;
111 mkdir $prefix unless -d
$prefix;
113 # On Win32, a very broken heuristics in Cabal forced us to fake a
114 # gcc-lib\ld.exe under pugs path.
115 my ($ghc_inst_path, $ghc_bin_path, $hsc2hs);
116 if ($^O
eq 'MSWin32') {
117 foreach my $args (@
{$opts->{SETUP
}}) {
118 $args =~ /^--with-hsc2hs=((.*[\\\/]).*)/ or next;
120 $ghc_inst_path = $ghc_bin_path = $2;
121 $ghc_inst_path =~ s{[/\\]bin[/\\]?$}{};
122 $ENV{PATH
} = "$ENV{PATH};$ghc_inst_path;$ghc_bin_path";
124 if (!-e
"gcc-lib/ld.exe" and -e
"$ghc_inst_path/gcc-lib/ld.exe") {
126 copy
("$ghc_inst_path/gcc-lib/ld.exe" => "gcc-lib/ld.exe");
129 die "Cannot obtain gcc-lib/ld.exe" unless -e
"gcc-lib/ld.exe";
130 warn "GHC installation path: $ghc_inst_path\n";
131 warn "GHC bin path: $ghc_bin_path\n";
132 warn "Runcompile: $runcompiler\n";
135 foreach my $args (@
{$opts->{SETUP
}}) {
136 $args =~ /^--with-hsc2hs=((.*[\\\/]).*)/ or next;
142 $hsc2hs ||= $ENV{HSC2HS
};
143 $AR_EXE = $Config{full_ar
} || File
::Spec
->catfile($ghc_bin_path, "ar$Config{_exe}");
145 my @configure_args = (
146 ($want_profiling ?
'--enable-library-profiling' : ()),
147 '--with-compiler=' . $runcompiler,
148 '--with-hc-pkg=' . $hc_pkg,
149 '--with-hsc2hs=' . $hsc2hs,
150 '--prefix=' . $prefix
156 chdir "third-party/judy/Judy-1.0.3";
157 copy('src/Judy.h', '../../HsJudy');
159 if ($^O eq 'MSWin32') {
161 $ENV{CC} = "$ghc_inst_path\\gcc";
162 $ENV{COPT} = "-I$ghc_inst_path\\include\\mingw -I$ghc_inst_path\\gcc-lib\\include " .
163 "-B$ghc_inst_path\\gcc-lib";
164 warn "\nCC = $ENV{CC}\nCOPT = $ENV{COPT}\n";
165 system("nmake /F Makefile.win32 /NOLOGO");
168 #if (!-e "src/obj/.libs/libJudy.a") {
169 my $make = $Config{make};
171 # Judy at this moment wants GNU make.
172 $make = 'gmake' unless `$make --version` =~ /GNU/;
174 system("./configure") unless -e "src/Makefile";
175 #system("$make clean");
179 #mkdir("../../installed") if !-d "../../installed";
181 #copy('src/obj/.libs/libJudy.a', '../../installed') unless -e '../../installed/libJudy.a';
182 #copy('src/obj/.libs/libJudy.a', '../../HsJudy') unless -e '../../HsJudy/libJudy.a';
189 foreach my $module (qw
< HsSyck hsregex
>) {
190 if ( my ($archive_dir) = (
191 glob("third-party/installed/*/$module-*"),
192 glob("third-party/installed/*/pugs-$module-*"),
194 my $_a = ($want_profiling ?
'_p.a' : '.a');
195 my $oldest_a_file = max
(
197 glob("$archive_dir/*$_a"),
198 glob("$archive_dir/*/*$_a"),
199 glob("$archive_dir/*/*/*$_a"),
205 return unless /\.hsc?$/ or /\.cabal$/;
206 $newest_hs_file = -M
$_ if !$newest_hs_file or -M
$_ < $newest_hs_file;
208 find
$wanted, "third-party/$module";
210 if ($newest_hs_file and $oldest_a_file and $newest_hs_file >= $oldest_a_file) {
211 # We are safe - no rebuild needed, but expose anyway
212 print "*** Skipping building the '$module' dependency.\n\n";
213 system($hc_pkg, expose
=> "pugs-$module");
218 chdir "third-party/$module";
220 warn join ' ', ("../../Setup$Config{_exe}", 'configure', @configure_args), $/;
221 if (-e
'.setup-config') {
222 system("../../Setup$Config{_exe}", 'configure', @configure_args);
223 system("../../Setup$Config{_exe}", 'unregister');
224 system("../../Setup$Config{_exe}", 'clean');
227 system("../../Setup$Config{_exe}", 'configure', @configure_args);
229 print "*** Building the '$module' dependency. Please wait...\n\n";
231 system("../../Setup$Config{_exe}", 'build');
232 system("../../Setup$Config{_exe}", 'install');
235 my ($archive_dir) = (
236 glob("third-party/installed/*/pugs-$module-*"),
237 glob("third-party/installed/*/$module-*"),
238 glob("third-party/installed/pugs-$module-*"),
239 glob("third-party/installed/$module-*"),
240 ) or die "Installation failed for $module";
243 glob("$archive_dir/*.a"),
244 glob("$archive_dir/*/*.a"),
245 glob("$archive_dir/*/*/*.a"),
247 system($AR_EXE, s
=> $a_file) unless $^O
eq 'MSWin32';
250 system($hc_pkg, expose
=> "pugs-$module");
255 # Embedding Judy object files in HsJudy
256 my ($archive_dir) = (
257 glob("third-party/installed/*/pugs-HsJudy-*"),
258 glob("third-party/installed/*/HsJudy-*"),
259 glob("third-party/installed/pugs-HsJudy-*"),
260 glob("third-party/installed/HsJudy-*"),
263 my @archive_files = (
264 glob("$archive_dir/*.a"),
265 glob("$archive_dir/*/*.a"),
266 glob("$archive_dir/*/*/*.a"),
269 my @o_files = map { glob("third-party/judy/Judy-1.0.3/src/$_/*.o"), }
270 qw( Judy1 JudyHS JudyCommon JudyL JudySL );
272 print "Embedding @o_files into @archive_files\n";
273 system($AR_EXE, "-r", $_, @o_files) for @archive_files;
275 if ($Config{ranlib} ne ':') {
276 system(split(/ /,$Config{ranlib}), $_) for @archive_files;
281 print "*** Finished building dependencies.\n\n";
283 $run_setup = sub { system($setup, @_) };
284 $run_setup->('configure', '--user', @configure_args, grep { !/^--.*=$/ } @
{$opts->{SETUP
}});
286 build_lib
($version, $ghc, @args);
288 # Cabal wants to copy LICENSE/GPL-3 to .../LICENSE/GPL-3
289 mkdir "third-party/installed/share/doc/Pugs-$version";
290 mkdir "third-party/installed/share/doc/Pugs-$version/LICENSE";
291 $run_setup->('install');
293 if ($Config{ranlib
} ne ':') {
294 system(split(/ /,$Config{ranlib
}), $_)
295 for glob("third-party/installed/lib/Pugs-$version/*.a");
298 build_exe
($version, $runcompiler, $ghc_version, @args);
300 if ($want_profiling) {
302 build_exe
($version, $runcompiler, $ghc_version, @args);
305 # determine if prelude wants be precompiled first,
306 # then does so if it needs to be
307 return unless PugsBuild
::Config
->lookup('precompile_prelude');
309 my $ppc_yml = "blib6/lib/Prelude.pm.yml";
314 src/Pugs/AST/Internals/Instances.hs
319 if ((!-s
$ppc_yml) or grep { -e
$_ and (-M
$ppc_yml > -M
$_) } @depends) {
320 # can't assume blib6/lib exists: the user may be running
321 # `make unoptimised` which doesn't create it.
322 mkpath
(dirname
($ppc_yml));
324 # also, remove all .pm.yml files there too.
325 unlink($_) for glob(File
::Spec
->catfile(dirname
($ppc_yml), "*.pm.yml"));
327 # finally regenerate the prelude.
328 run
($^X
, qw
<util
/gen_prelude.pl -v -i src/perl
6/Prelude
.pm
>,
329 (map { ('-i' => $_) } @
{ PugsBuild
::Config
->lookup('precompile_modules') }),
330 '-p', $thispugs, '--output', $ppc_yml);
336 require Compress
::Zlib
;
337 open my $ifh, "<", $in or die "open: $in: $!";
338 open my $ofh, ">", $out or die "open: $out: $!";
340 my $gz = Compress
::Zlib
::gzopen
($ofh, "wb") or
341 die "gzopen: $Compress::Zlib::gzerrno";
343 $gz->gzwrite($_) or die "gzwrite: $Compress::Zlib::gzerrno";
354 my @a_file = File
::Spec
->rel2abs("dist/build/libHSPugs-$version.a");
355 push @a_file, File
::Spec
->rel2abs("dist/build/libHSPugs-${version}_p.a") if $want_profiling;
358 local $ENV{PATH
} = dirname
($ghc) . $Config{path_sep
} . $ENV{PATH
};
360 run
($^X
, qw
<util
/version_h
.pl
>);
362 mkdir "dist/build" unless -d
"dist/build";
364 # Remove all -boot files since GHC 6.4 doesn't track them.
365 # This is not needed for GHC 6.5 which doesn't produce them anyway.
366 # Also, remove Version.o and Version.hi so -v will report correctly.
368 return unless $_ =~ /-boot$/ or $_ =~ /Version\.\w+$/;
371 find
$wanted, "dist/build";
373 unlink $_ for @a_file;
374 $run_setup->('build');
375 (-e
or die "Build failed for '$_': $?") for @a_file;
378 my $module = shift; # eg. "Data.Yaml.Syck"
379 my $pathname = $module;
380 $pathname =~ s!\.!/!g;
381 $pathname .= '_stub.o';
382 my $basename = $pathname;
383 $basename =~ s!.*/!!;
385 # XXX - work around Cabal bug --
386 # we have to locate "Syck_stub.o" and copy it into
387 # dist/build/src/Data/Yaml/.
389 my $target = File
::Spec
->canonpath(
390 File
::Spec
->catfile(qw
< dist build src
>, $pathname)
393 return unless $_ eq $basename;
394 push @candidates, $File::Find
::name
;
396 find
$wanted, "dist";
398 if (@candidates > 1) {
399 # This is harmless -- so we don't do anything.
400 # warn "*** Found more than one '$basename' -- using the first one. \n";
402 elsif (@candidates == 0) {
403 warn "*** Wasn't able to find '$basename' (this may be a problem)...\n";
407 unless( File
::Spec
->canonpath($candidates[0]) eq $target ) {
408 mkpath
(($target =~ m!(.*[/\\])!)[0]); # create dir for target
409 copy
($candidates[0] => $target)
410 or die "Copy '$candidates[0]' => '$target' failed: $!";
414 print "==> $AR_EXE r $_ $target\n";
415 system($AR_EXE, r
=> $_, $target);
419 $fixup->('Pugs.Embed.Perl5') if grep /^-DPUGS_HAVE_PERL5$/, @_;
420 $fixup->('Pugs.Embed.Parrot') if grep /^-DPUGS_HAVE_PARROT$/, @_;
422 foreach my $a_ext (grep { /\.a$/ and !/^-/ } @_) {
423 # Do some very sneaky things -- linking other .a with us!
424 my $basename = $a_ext;
425 $basename =~ s!.*/!!;
426 my $dir = "dist/tmp-$basename";
429 system($AR_EXE, x
=> $a_ext);
431 print "==> $AR_EXE r $_ @{[glob('*')]}\n";
432 system($AR_EXE, r
=> $_, glob("*"));
441 if ($Config{ranlib
} ne ':') {
442 system(split(/ /,$Config{ranlib
}), $_) for @a_file;
447 map { +("-package" => $_) } @_;
453 my $ghc_version = shift;
455 my @pkgs = '-hide-all-packages';
457 my $push_pkgs = sub { push @pkgs, packages
(@_) };
459 #my @o = qw( src/pcre/pcre.o src/syck/bytecode.o src/syck/emitter.o src/syck/gram.o src/syck/handler.o src/syck/implicit.o src/syck/node.o src/syck/syck.o src/syck/syck_st.o src/syck/token.o src/syck/yaml2byte.o src/cbits/fpstring.o );
460 #push @o, 'src/UnicodeC.o' if grep /WITH_UNICODEC/, @_;
461 #system $ghc, '--make', @_, @o, '-o' => 'pugs', 'src/Main.hs';
464 stm network mtl filepath base HsSyck
465 containers bytestring random process directory
466 time array pretty parsec template
-haskell
470 if ($^O
=~ /(?:MSWin32|mingw|msys|cygwin)/) {
471 $push_pkgs->('Win32');
474 $push_pkgs->('unix');
476 $push_pkgs->('readline') if grep /^-DPUGS_HAVE_READLINE$/, @_;
477 $push_pkgs->(qw(plugins haskell-src)) if grep /^-DPUGS_HAVE_HSPLUGINS$/, @_;
478 my @libs = "-lHSPugs-$version" . ($want_profiling ?
'_p' : '');
479 push @libs, grep /^-opt/, @_;
480 push @libs, grep /^-[lL]/, @_;
481 push @libs, grep /\.(?:a|o(?:bj)?|\Q$Config{so}\E)$/, @_;
482 push @libs, grep /^-auto/, @_;
484 # XXX - Hack to work around Cabal's semibroken profiling lib support!
485 my $out = "pugs$Config{_exe}";
487 if ($want_profiling) {
488 $out = "pugs-prof$Config{_exe}";
490 push @pkgs, glob('third-party/HsSyck/dist/build/syck/*.o'), qw( third-party/hsregex/dist/build/pcre/pcre.o );
493 push @libs, grep /^-threaded/, @_;
496 # Force relinking of Main.hs to avoid stale dependencies in .hi
498 unlink 'src/Main.hi';
500 @_ = ('--make', @pkgs, qw(-optl-Lthird-party/installed -o ), "$out.new", qw( src/Main.hs ), @libs);
501 #@_ = (@pkgs, qw(-idist/build -Ldist/build -idist/build/src -Ldist/build/src -o pugs src/Main.hs), @libs);
502 print "*** Building: ", join(' ', $ghc, @_), $/;
505 die "Build failed: $?" unless -e
"$out.new";
508 unlink $out or die "Cannot remove $out: $!";
511 rename "$out.new" => $out;
514 sub write_buildinfo
{
515 my ($version, $ghc, $ghc_pkg, $ghc_version, @args) = @_;
517 open IN
, "< Pugs.cabal.in" or die $!;
518 open OUT
, "> Pugs.cabal" or die $!;
521 my $add_dep = sub { $depends .= ", $_" for @_ };
524 $add_dep->(@_) if grep /^-D\Q$arg\E$/, @args;
527 if ($^O
=~ /(?:MSWin32|mingw|msys|cygwin)/) {
528 $add_dep->('Win32 -any');
531 $add_dep->('unix -any');
534 $cond_dep->('PUGS_HAVE_HSPLUGINS', 'plugins -any', 'haskell-src -any');
535 $cond_dep->('PUGS_HAVE_READLINE', 'readline -any');
538 if (grep /^-DPUGS_HAVE_PERL5$/, @args) {
539 $perl5_c = 'src/perl5/p5embed.c';
543 if (grep /^-DPUGS_HAVE_PARROT$/, @args) {
544 $parrot_c = 'src/pge/parrotembed.c';
547 # Remove -Wl flags in Perl5 embedding.
548 @args = grep { !/^-W/ } @args;
550 # Remove -threaded if we are building profiled.
551 @args = grep { !/^-threaded/ } @args if $want_profiling;
553 my @include_dirs = grep { -d
$_ }
554 map File
::Spec
->canonpath(substr($_, 2)),
556 my @lib_dirs = grep { -d
$_ }
557 map File
::Spec
->canonpath(substr($_, 2)),
559 my @libs = map substr($_, 2), grep /^-l/, @args;
560 #push @libs, grep /\.(?:a|o(?:bj)?)$/, @args;
562 my $has_new_cabal = (`$ghc_pkg describe Cabal` =~ /version: 1\.[1-9]/i);
565 # Adjust the dependency line based on Cabal version
567 s/pugs-hsregex -any, //;
568 s/__OPTIONS__/@args/;
569 s/__VERSION__/$version/;
570 s/__DEPENDS__/$depends/;
571 s/__PERL5_C__/$perl5_c/;
572 s/__PARROT_C__/$parrot_c/;
573 s/__INCLUDE_DIRS__/@include_dirs/;
575 s/__LIB_DIRS__/@lib_dirs/;
583 sub classify_options
{
586 # we can't use +SEGMENT and -SEGMENT since that interferes with GHC.
587 $kind = $1, next if /^_\+(.*)/; # _+SEGMENT start
588 undef $kind, next if $_ eq "_-$kind"; # _-SEGMENT end
590 s/^__(.*)__$/PugsBuild::Config->lookup($1)/e;
592 die "don't know where this option belongs: $_" unless $kind;
593 push @
{ $opts{$kind} }, $_;
599 print ((join " ", @_) . "\n");
600 system @_ and die (sprintf "system: [%s]: $!", join " ", @_);
604 my ($src, $dest) = @_;
607 opendir(DIR
, $src) or die $!;
608 my @nodes = readdir(DIR
);
609 foreach my $node (sort @nodes) {
610 next if $node =~ /^(\.|\.\.|\.svn|src)$/;
611 my $src_path = "$src/$node";
612 my $dest_path = "$dest/$node";
614 copy
($src_path, $dest_path);
617 copy_all
($src_path, $dest_path);