package Math::PariBuild; $VERSION = '2.01080604'; require Exporter; @ISA = 'Exporter'; @EXPORT = qw(get_pari_version pari_formatted_version find_pari_dir download_pari patch_pari download_and_patch_pari make_pod build_tests find_paricfg find_or_Configure_paricfg write_paricfg build_paricfg find_machine_architecture known_asmarch inline_headers inline_headers2 not_gnu_as choose_and_report_assembler kernel_files kernel_fill_data assembler_flags assembler_flags_via extra_includes ep_codes_from_file ep_hash_report ep_in_version code_C_translator build_funclists ); use strict; use Config; use File::Copy 'copy'; use File::Basename 'basename'; =head1 NAME Math::PariBuild - utility functions used during configuration of C. =head1 SYNOPSIS use Math::PariBuild; =head1 DESCRIPTION =over =item C extracts the version of GP/PARI given the build directory $dir; version is read from $F<$dir/config/version> and has the form as in C<"2.3.1">. Returns undef on failure. =cut sub get_pari_version { my $dir = shift; my $v = ""; open(IN, "$dir/config/version") or return; /(?:version|VersionMajor|VersionMinor|patch)='?(\d+(\.\d+)?)'?/ and $v .= "$1." while ; close(IN) or die "error closing '$dir/config/version'"; $v =~ s/\.$// or return; return $v; } =item C extracts the version of GP/PARI given the build directory $dir; version has the form as in C<"2003001"> for version 2.3.1. Returns the directory name on failure. =cut sub pari_formatted_version { my $dir = shift; my $v; $v = get_pari_version $dir; if (defined $v) { $dir = $v; } else { warn(<; @dirs = "$dir/pari" if not @dirs and -d "$dir/pari"; @dirs = grep -e "$_/$latmus", @dirs; last if @dirs; } @gooddirs = grep !/alpha|beta/, @dirs; @gooddirs = grep !/alpha/, @dirs unless @gooddirs; @gooddirs = @dirs unless @gooddirs; @gooddirs = sort {pari_formatted_version($a) cmp pari_formatted_version($b)} @gooddirs; return $gooddirs[-1]; } =item download_pari() Using FTP connection, downloads the latest version of GP/PARI, and extracts it. Returns the GP/PARI build directory and the version in the format C<"2.3.1">. [Currently skips versions 2.3.*] Optional arguments: name of the tar file with GP/PARI source (undef OK), force download. =cut sub manual_download_instructions { <content; my $t = $r->content_type; $c = '<>' unless defined $c; $c =~ s/\s+\Z/\n/; my $b = '====================='; return "\n$b Response content (type=$t)\n$c\n$b\n\n"; } sub ll_ftp () { # All Perl download failures I saw are on Linux and BSD. # this (not very portable) solution should work there? open OF, '> ftp-cmd' or die "Can't open `ftp-cmd' for write: $!"; print OF <<'EOF'; # XXXX Hardwired version! user anonymous auto-download-Math-Pari@cpan.org cd /pub/pari/unix/ dir cd OLD dir binary get pari-2.1.7.tgz quit EOF close OF or die "Can't close `ftp-cmd' for write: $!"; print <($srcfile, 'ok2.3'); } else { if ($force) { print "Forced autofetching...\n\n" } elsif (not $ENV{AUTOMATED_TESTING} and not $ENV{PERL_MM_USE_DEFAULT} and -t STDIN and (-t STDOUT or -p STDOUT)) { # Interactive $| = 1; # Usually, we run under MakeMaker, so test PERL_MM_USE_DEFAULT my $mess = <; if ($ans !~ /y/i) { my ($eA, $eM, $tI, $tO, $tE, $pO, $pE) = (@ENV{ qw(AUTOMATED_TESTING PERL_MM_USE_DEFAULT) }, -t STDIN, -t STDOUT, -t STDERR, -p STDERR, -p STDOUT); defined() ? $_ = "'$_'" : $_ = '' for $eA, $eM; print <new($host) or die "Cannot create FTP object: $!"; $ftp->login("anonymous","Math::Pari@") or die "Cannot login anonymously (",$ftp->message(),"): $!"; my $c = 0; my @Extra = @extra_chdir; while (not $c) { $ftp->cwd($dir) or die "Cannot cwd (",$ftp->message(),"): $!"; $ftp->binary() or die "Cannot switch to binary (",$ftp->message(),"): $!"; my @lst = $ftp->ls(); @lst or ($ftp->pasv() and @lst = $ftp->ls()) or die "Cannot list (",$ftp->message(),"): $!"; #print "list = `@lst'\n"; %archive = (); for my $file (@lst) { $c++ if $match_pari_archive->($file); } unless ($c) { die "Did not find any file matching /$match/ via FTP\n\n" . manual_download_instructions() unless @Extra; $dir = shift @Extra; print "Not in this directory, now chdir('$dir')...\n"; } } }; if ($@) { undef $ftp; warn "$@\nCan't fetch file with Net::FTP, now trying with LWP::UserAgent...\n"; # second try with LWP::UserAgent eval { require LWP::UserAgent; require HTML::LinkExtor } or die "You do not have LWP::UserAgent and/or HTML::LinkExtor installed, cannot download, exiting...\n\n" . manual_download_instructions(); my $c = 0; my @Extra = @extra_chdir; while (not $c) { $ua = LWP::UserAgent->new; $ua->env_proxy; my $req = HTTP::Request->new(GET => $base_url); my $resp = $ua->request($req); $resp->is_success or die "Can't fetch directory listing from $base_url: " . $resp->as_string; %archive = (); if ($resp->content_type eq 'text/html') { my $p = HTML::LinkExtor->new; $p->parse($resp->content); for my $link ($p->links) { my($tag, %attr) = @$link; next if $tag ne 'a'; $c++ if $match_pari_archive->($attr{href}); } } else { foreach my $file (split /\n/, $resp->content) { $c++ if $match_pari_archive->($file); } } unless ($c) { unless (@Extra) { warn debug_no_response($resp) . "Did not find any file matching /$match/ via FTP.\n\n"; my $f = ll_ftp or die manual_download_instructions(); return download_pari($f); } my $dir = shift @Extra; $base_url .= "$dir/"; print "Not in this directory, trying `$base_url'...\n"; } } } } sub fmt_version {sprintf "%03d%03d%03d", split /\./, shift} my ($type, %have, %types, $best, %latest_version, %latest_file); for $type (qw(alpha beta golden)) { if ($archive{$type}) { $have{$type}++; $best = $type; my @files = keys %{$archive{$type}}; print "Available $type versions: `@files'\n"; $latest_version{$type} = (sort {fmt_version($a) cmp fmt_version($b)} keys %{$archive{$type}})[-1]; $latest_file{$type} = $archive{$type}{$latest_version{$type}}; print qq(Latest supported $type is `$latest_file{$type}'\n); } } # Special-case v2.0.14 if (!$archive{golden} and $latest_version{beta} eq '2.0.11' and $latest_version{alpha} eq '2.0.14') { $best = 'alpha'; # It is tested! } undef $dir; my $version; if ($best) { my $file = $latest_file{$best}; $version = $latest_version{$best}; print qq(Picking $best version $version, file $file\n); if (-f $file) { print qq(Well, I already have it, using the disk copy...\n); } else { print qq(Downloading `$base_url$file'...\n); if ($ftp) { $ftp->get($file) or die "Cannot get via FTP (",$ftp->message(),"): $!"; $ftp->quit or warn "Warning: cannot quit FTP: ", $ftp->message(); } else { my $req = HTTP::Request->new(GET => "$base_url$file"); my $resp = $ua->request($req); $resp->is_success or die "Can't fetch $base_url/$file: " . $resp->as_string; my $base = basename($file); open(F, ">$base") or die "Can't write to $base: $!"; binmode F or die "Can't binmode(): $!"; print F $resp->content; close F; } print qq(Downloaded...\n); } print qq(Extracting...\n); my $zcat = "gzip -dc"; # zcat may be the old .Z-extractor print "$zcat $file | tar -xvf -\n"; system "$zcat $file | tar -xvf -" and do { print "Can't un-targz PARI: \$!=$!, exitcode=$?.\n"; my @cmd = ($^X, qw(-MArchive::Tar -wle), 'Archive::Tar->new(shift)->extract()', $file); print ' Now retry with "', join('" "', @cmd), "\"\n"; system @cmd and die "Can't un-targz PARI: \$!=$!, exitcode=$?.\n" }; ($dir = $file) =~ s,(?:.*[\\/])?(.*)\.t(ar\.)?gz$,$1, or die "malformed name `$file'"; -d $dir or die "Did not find directory $dir!"; } return ($dir, $version); } =item C Returns patches appropriate for GP/PARI version $version (formatted as in C<2.2.2>). =cut sub patches_for ($) { my ($v) = (shift); my %patches = ('2.0.11' => [qw( patch11/diff_pari_gnuplot_aa patch11/patch_pari_round0 patch11/patches_round1_short patch11/diff_pari_fixed_interfaces_011 patch11/diff_pari_highlevel_hash_011a patch11/diff_pari_ret_proto_2011)], '2.0.12' => ['patch12/diff_for_perl_2012'], '2.0.13' => ['patch13/diff_for_perl_2013', 'patch13/diff_for_gnuplot_2013'], '2.0.14' => ['patch14/diff_for_perl_2014', 'patch14/diff_extra_2014', 'patch14/diff_last_2014', 'patch14/diff_plot_2014'], '2.0.15' => ['patch15/diff_cast_2015', 'patch15/diff_errout_2015', 'patch15/diff_gnuplot_2015', 'patch15/diff_proto_2015', 'patch15/diff_errpari_2015', 'patch15/diff_pari_gnuplot_2015'], '2.0.16' => ['patch16/diff_gnuplot_2016'], '2.1.2' => ['patches/diff_2.1.2_gccism'], '2.1.3' => ['patches/diff_2.1.3_interface'], '2.1.4' => ['patches/diff_2.1.4_interface'], '2.1.5' => ['patches/diff_2.1.4_interface'], '2.2.2' => ['patches/diff_2.2.2_interface'], '2.1.6' => ['patches/diff_2.1.6_ploth64', 'patches/diff_2.1.6_align_power_of_2', 'patches/diff_2.1.6_no-common'], '2.1.7' => [ ($^O =~ /darwin/i ? 'patches/diff_2.1.6_no-common' : ()), ($^O eq 'MSWin32' ? 'patches/diff_2.1.7_mingw-w64' : ()), 'patches/patch-pari-unnormalized-float', 'patches/diff_2.1.7_-O', 'patches/diff_2.1.7_div', 'patches/diff_2.1.6_align_power_of_2', 'patches/diff_2.1.7_restart'], '2.3.5' => [ ($^O eq 'MSWin32' ? 'patches/diff_2.3.5_mingw-w64' : ())], ); print "Looking for patches for $v...\n"; my @p = $patches{$v} ? @{$patches{$v}} : (); push @p, 'patches/diff_pari-2.1.3-ix86-divl' if $v le '2.1.3' or $v ge '2.2' and $v le '2.2.2'; @p; } =item C Applies known necessary fixes to GP/PARI build directory $dir if needed. Returns empty if no patching is needed, otherwise the string encoding return values of patch commands. =cut sub patch_args ($) { return '/' unless $^O =~ /win32/i; my($patch, $p) = (shift, 'utils/inc_h.diff'); $p =~ s,/,\\,g; system "$patch -p1 --binary < $p" or warn("... Apparently, your patch takes flag --binary...\n"), return ('\\', '--binary'); return '\\'; } sub patch_pari { my ($dir, $version) = (shift, shift); $version = get_pari_version($dir) unless defined $version; my @patches = patches_for($version) or return; print "Patching...\n"; my $patch = $Config{gnupatch} || 'patch'; my ($dir_sep, @args) = patch_args $patch; my ($rc, $p) = join '; ', $dir_sep, @args, ''; foreach $p (@patches) { (my $pp = "../$p") =~ s,/,$dir_sep,g; my $cmd = "cd $dir && $patch -p1 @args < $pp"; print "$cmd\n"; system "$cmd" and warn "...Could not patch: \$?=$?, $!; continuing anyway...\n"; $rc .= "'$pp' => $?, " } print "Finished patching...\n"; $rc =~ s/,?\s+$//; $rc } =item download_and_patch_pari() Using FTP connection, downloads the latest version of GP/PARI, extracts it, and applies known necessary fixes if needed. Returns the GP/PARI build directory (in scalar context), otherwise the directory and the result of patching. Same optional arguments as for download_pari(). =cut sub download_and_patch_pari { my ($file, $force, @rc) = (shift, shift); my ($dir, $version) = download_pari($file, $force); @rc = patch_pari($dir, $version) if defined $dir; return $dir unless wantarray; ($dir, @rc); } =item C Makes POD documentation for functions in the PARI library. Converts the TeX file found in GP/PARI build directory $dir to POD using the given options for F. =cut # We can't do what the commented chunk does: $paridir/doc/gphelp is # auto-generated, so its date is not relevant to anything. # $targ = 'libPARI/gphelp'; # if (not -e $targ # or -M $targ > -M "$paridir/doc/gphelp") { # if (-f $targ) { # chmod 0666, $targ; # unlink $targ; # } # copy "$paridir/doc/gphelp", $targ; # } sub make_pod { my ($targ, $how, $paridir) = @_; if (not -e $targ or -M $targ > -M "$paridir/doc/usersch3.tex" or -M $targ > -M "libPARI/gphelp") { if (-f $targ) { chmod 0666, $targ; unlink $targ; } (system "$^X libPARI/gphelp $how $paridir/doc/usersch3.tex > tmp_pod " and (warn("Errors when converting documentation: $?"), 0)) or rename 'tmp_pod', $targ; } } sub scan_headers { my $opts = shift; warn "Scanning header files...\n"; my $cmd = "$Config{cpprun} $Config{cppflags} utils/inc.h 2>&1"; open INC, "$cmd |" or warn("Error $! from: $cmd\n"), return; $opts->{clk_tck_def} = 1; while () { $opts->{have_ulong} = 1, warn "...ulong\n" if /\btypedef\b.*\bulong\s*;/; $opts->{clk_tck_def} = 0, warn "...CLK_TCK not defined\n" if /y\s*=\s*CLK_TCK\b/; $opts->{have_getrusage} = 1, warn "...getrusage\n" if /\bgetrusage\s*\(/; $opts->{have_ladd} = 1, warn "...ladd\n" if /\bladd\b/; } close INC or warn "Note (probably harmless): Errors reading from pipe: '$!', exit=$?: $cmd\n" } =item C Converts GP/PARI test files in GP/PARI build directory $dir to Perl test suite. =cut sub build_tests { my $dir = shift; my $paritests = "$dir/src/test/in"; opendir TESTS, $paritests or die "Cannot find tests in $paritests: $!"; my @tests = readdir TESTS; closedir TESTS or die "Cannot find tests (close): $!"; my $sou = 'test_eng/ex.t'; my $targ = "$sou-"; unless (-e $targ and -M $targ <= -M $sou) { $dir =~ s/\\/\\\\\\\\/g; my $quote = ($^O =~ /win32/i) ? '"' : "'"; system "$^X -pe $quote s,CHANGE_ME,$dir, $quote $sou > $targ" and die "Could not run test converter: $! $?"; } $sou = $targ; my $test; for $test (@tests) { next if $test =~ /^\.\.?$/; next if $test =~ /compat/; next if -d "$paritests/$test" and $test eq 'CVS'; next if $test =~ /(~|\.(bak|orig|rej))$/; $targ = "t/55_$test.t"; if (-f $targ) { chmod 0666, $targ; unlink $targ; } copy $sou, $targ or die "Cannot create test $test.t: $1"; } } =item C Finds suitable (?) files F in GP/PARI build directory $dir. =cut sub find_paricfg { my $paridir = shift; my @paricfg = <$paridir/o.*/paricfg.h>; push @paricfg, <$paridir/O*/paricfg.h>; # Reported to work with Win32 too @paricfg = grep !/Odos/, @paricfg unless $^O =~ /dos|djgcc|MSWin32/i; # Probably not present in newer versions anymore... unshift @paricfg, "$paridir/win32/paricfg.h" if $^O eq 'MSWin32' and -f "$paridir/win32/paricfg.h"; @paricfg; } =item C Finds suitable (?) files F in GP/PARI build directory $dir. If $do_configure is true, runs GP/PARI's Configure script to build one. Returns FALSE if F needs to be build by Perl. =cut sub find_or_Configure_paricfg { my ($paridir, $do_configure) = (shift, shift); my @paricfg = find_paricfg $paridir; return 0 unless $do_configure; if (@paricfg == 0) { print "No existing paricfg.h found, running Configure...\n"; print "cd $paridir ; sh ./Configure\n"; system "cd $paridir ; sh ./Configure" and die "Cannot configure: $!, exitcode=$?.\n"; print "Configuration of GP/PARI successful.\n"; @paricfg = find_paricfg $paridir; } if (@paricfg == 0) { warn < 1) { warn "Found multiple paricfg.h: @paricfg.\n"; @paricfg = sort { -M $a <=> -M $b} @paricfg; $found = $paricfg[0]; warn "Choosing newest paricfg.h: $found.\n"; } if (-e 'libPARI/paricfg.h' and -M $found >= -M 'libPARI/paricfg.h') { print <. Returns hash with options found during the scan of the header files. =cut sub write_paricfg { my $version = shift; my %opts; scan_headers(\%opts) or $opts{clk_tck_def} = 0; warn "Creating libPARI/paricfg.h...\n"; open F, '> libPARI/paricfg.h' or die "open 'libPARI/paricfg.h' for write: $!"; print F < EOP print F < Builds F either ourselves, or by looking for it in GP/PARI build directory $dir - and running GP/PARI's Configure script if needed. Returns hash with options found during the scan of the header files. =cut sub build_paricfg { # $version as in 2003005 for 2.3.5 my ($paridir, $do_configure, $version) = (shift, shift, shift); my %opts; unless (find_or_Configure_paricfg($paridir, $do_configure)) { # Not generated by Configure if (-r 'libPARI/paricfg.h') { print <; close IN or die "close /proc/cpuinfo: $!"; $machine = process_sparc $info, $machine; } } elsif ($os eq 'sunos') { my $type = (split ' ', $Config{myuname})[4]; # format: SunOS name 5.9 Generic_118558-26 sun4u sparc SUNW,Ultra-5_10 # But Generic* part can be skipped??? $type = (split ' ', $Config{myuname})[3] if $type eq 'sparc'; my $redo; find_machine: { if ($type =~ /^sun3/) { $machine = 'm68k'; } elsif ($type =~ /^sun4[dm]/) { local $ENV{PATH} = "$ENV{PATH}:/dev/sbin"; my $info = `(prtconf||devinfo)2>&-`; $info = join ' ', grep /(TI|FMI|Cypress|Ross),/, split "\n", $info; $machine = process_sparc $info, 'sparcv8'; } elsif ($type eq 'sun4u') { $machine = 'sparcv9'; } elsif ($type =~ /^sun4[ce]?$/) { # $machine = 'sparcv7'; $machine = 'none'; # sparcv7 not available with 2.3.0 } elsif ($type =~ /^i.*pc$/) { $machine = 'ix86'; } elsif ((split ' ', $Config{myuname})[3] eq 'sun') { $machine = 'm86k'; } elsif ($redo++ == 0) { $type = `uname -m`; redo find_machine; } } } elsif ($os eq 'gnu') {# Cover GNU/Hurd, GNU/kFreeBSD and other GNU userland chomp($machine = `uname -m`); $machine = 'ix86' if $machine =~ /^i\d86-/; } $machine = 'port' # No assembler for 64bit - unless alpha/ia64 if $machine !~ m(alpha|64) and ($Config{longsize} || 0) == 8; print("I detect multi-arch build; assembler not supported on such builds.\n\n"), $machine = 'port' # No assembler for repeated -arch (multi-arch build) if $Config{ccflags} =~ /(^|\s)-arch\s.*\S\s+-arch\s/; # For older PARI: ### $machine = 'sparcv8super' ### if $machine eq 'sparcv9' or $machine eq 'sparcv8_hyper' ### or $machine eq 'sparcv8_super'; ### $machine = 'sparcv8micro' if $machine eq 'sparcv8_micro'; # This part is probably not needed and never entered if (not defined $machine and $Config{myuname} =~ /\b(sun3|sparcv7|sparcv8_micro|sparcv8_super|alpha|hppa|[ix]\d86)\b/) { $machine = $1; } elsif (not defined $machine) { chomp($machine = `uname -m`); } $machine =~ s/[ix]\d86(-\w+)?/ix86/ if defined $machine; # i686-pc print "...Processor of family `$machine' detected\n"; return $machine; } sub not_gnu_as { local $/; my $ass = $ENV{AS} || 'as'; my $devnul = -e '/dev/null' ? '< /dev/null' : ''; open ASS, "$ass --version 2>&1 $devnul |"; my $assout; eval { local $SIG{ALRM} = sub {die}; eval {alarm 10}; # Be extra safe... $assout = ; close ASS; unless ($assout) { eval {alarm 10}; # Be extra safe... open ASS, "$ass -v 2>&1 $devnul |"; $assout = ; close ASS; } eval {alarm 0}; }; ($assout and $assout =~ /GNU/) and return; # GNU $assout or 1; } # Which files to catenate to produce pariinl.h. Apparently, the only # need to go to asm0.h in pre-2.3 is to undo the effect of ASMINLINE in # paricfg.h. Note that we do ASMINLINE from the command line. # 2.3.0 does something like this: # ../config/genkernel ../src/kernel/ix86/asm0.h > parilvl0.h # cat ../src/kernel/none/tune.h ../src/kernel/none/int.h ../src/kernel/none/level1.h > parilvl1.h # cat parilvl0.h parilvl1.h > pariinl.h # This logic works with up to 2.2.13 sub sparcv8_inl { my ($asmarch, $pari_version) = (shift, shift); return ['none/asm0.h','none/level1.h'] if $Config{osname} =~ /^(linux|nextstep)$/; return ['sparcv8/level0.h','none/level1.h'] if $pari_version < 2002006; return ['sparcv8_micro/level0_common.h','sparcv8_micro/level0.h', 'none/level1.h'] if $asmarch eq 'sparcv8_micro'; return ['sparcv8_micro/level0_common.h','none/divll.h', 'none/level1.h'] if $asmarch eq 'sparcv8_super'; # No for sparcv8... } sub inline_headers_arr { # These files are cat()ed to pariinl.h my ($asmarch, $pari_version) = (shift, shift); return sparcv8_inl($asmarch, $pari_version) if $asmarch =~ /^sparcv8/; my %h = ( alpha => ['none/asm0.h','none/level1.h'], hppa => ['none/asm0.h','none/level1.h'], ix86 => ['ix86/level0.h','none/level1.h'], m86k => ['none/level0.h','none/level1.h'], none => ['none/level0.h','none/level1.h'], # ppc is not done yet (2.0.15) ($pari_version > 2002007 ? (ppc => ['ppc/asm0.h', 'none/divll.h'], x86_64 => ['x86_64/asm0.h','none/level1.h'], ia64 => ['ia64/asm0.h','ia64/asm1.h']) : ()), sparcv7 => ['none/asm0.h','none/level1.h'], # sparcv8 => $sparcv8_inl, # sparcv8_micro => $sparcv8_inl, # sparcv8_super => $sparcv8_inl, # sparcv9 is not done yet (2.0.15) ); $h{$asmarch}; } sub inline_headers { my ($asmarch, $pari_version) = (shift, shift); my $inlines = inline_headers_arr($asmarch, $pari_version) or die "Unknown inlines for '$asmarch'"; my @inlines = @$inlines; if ($pari_version < 2003000) { # Old, explicit logic unshift @inlines, 'none/int.h' if $pari_version >= 2002005; unshift @inlines, 'none/tune.h' if $pari_version >= 2002008; } map "\$(PARI_DIR)/src/kernel/$_", @inlines; } sub generic_build_method { my ($asmarch, $pari_version, $paridir) = (shift, shift, shift); $pari_version >= 2003000 and (not -f "$paridir/src/kernel/$asmarch/MakeLVL1.SH" or $asmarch eq 'none'); } sub inline_headers_by_file { # logic of 2.3.0 my($dir,$f) = (shift, shift); die "I expect to have $f present" unless -f $f; #local $/ = "\n"; open F, "< $f" or die "Error opening `$f' for read: $!"; my @I = ([], [$f]); # INL, NOINL while () { next unless /^(NO)?ASM\s+(\S.*?)\s*$/; # print "Found <$_>\n"; my $arr = $I[ $1 ? 1 : 0 ]; push @$arr, map "$dir/none/$_.h", split ' ', $2; } close F or die "Error closing `$f' for read: $!"; return @I; } sub inline_headers_by_dir { # logic of 2.3.0 my ($asmarch, $pari_version, $paridir) = (shift, shift, shift); my $dir = "$paridir/src/kernel"; #$asmarch ='none' if $asmarch eq 'port'; unless (generic_build_method($asmarch, $pari_version, $paridir)) { # die "Do not know how to process MakeLVL1.SH"; } my @I = ([],[]); @I = inline_headers_by_file $dir, "$dir/sparcv8_micro/asm0-common.h" if $asmarch =~ /^sparcv8_/; my @I1 = inline_headers_by_file $dir, "$dir/$asmarch/asm0.h"; for (0,1) { push @{$I[$_]}, @{$I1[$_]}; } push @{$I[1]}, map "$dir/none/$_.h", qw(tune int level1); return @I; } sub inline_headers_pre { my ($asmarch, $pari_version, $paridir) = (shift, shift, shift); return if $pari_version < 2003000; my $script = "$paridir/src/kernel/$asmarch/"; return; } sub inline_headers2 { my ($asmarch, $pari_version, $paridir) = (@_); return ([inline_headers_pre(@_)], [inline_headers(@_)]) if $pari_version < 2003000; return inline_headers_by_dir(@_); } sub known_asmarch { defined inline_headers_arr(@_); } sub choose_and_report_assembler { my($machine, $pari_version) = (shift, shift); my %asmarch = ( sun3 => 'm86k', sparc => 'sparcv8_micro', sparcv9 => 'sparcv8_micro', port => 'none', mips => 'none', fx2800 => 'none', ia64 => (($Config{longsize}||4) == 8 ? 'ia64' : 'none'), hppa => ($Config{osvers} =~ /^.\.10\./ ? 'hppa' : 'none'), ); my $asmarch = $asmarch{$machine} || $machine; # Temporary only my %skip64 = (alpha => 1, none => 1); if (not ( $skip64{$asmarch} or $asmarch =~ /\D64$/ ) and ($Config{longsize} || 0) == 8) { $asmarch .= '_64'; $asmarch = 'hppa64' if $asmarch eq 'hppa_64'; $asmarch = 'x86_64' if $asmarch eq 'ix86_64'; } unless (known_asmarch $asmarch, $pari_version) { warn <[2], $sparcv8_kernel->[3]] unless $sparcv8_need_kernel2; return $sparcv8_kernel; } sub sparcv7_kernel_files { my ($asmarch, $pari_version, $Using_gnu_as) = (shift, shift, shift); my $_ext = (($pari_version < 2000015) ? 's' : 'S'); my $cvt = $Using_gnu_as || $Config{osname} =~ /^(linux|nextstep|netbsd)$/; return ["sparcv7/level0.$_ext", $cvt]; } sub kernel_files { my ($asmarch, $pari_version, $Using_gnu_as, $paridir) = (@_); return [] if $pari_version >= 2003000; return sparcv8_kernel_files_old($asmarch, $pari_version, $Using_gnu_as) if $asmarch =~ /^sparcv8/ and $pari_version < 2002006; return sparcv7_kernel_files($asmarch, $pari_version, $Using_gnu_as) if $asmarch eq 'sparcv7'; my $sparcv8_kernel = ["sparcv8_micro/level0_common.S", 1, "$asmarch/level0.S", 1]; # 2.2.* only # Default ["$asmarch/level0.s", 0] my %level0 = ( alpha => '', hppa => '', # was ['none/level0.c', 0] before 2.0015 ($pari_version > 2002007 ? (ppc => ["none/level0.c", 0], ia64 => ["ia64/level0.s", 0]) : ()), ix86 => ['ix86/l0asm.c', 1], m86k => ["none/level0.c", 0], none => ["none/level0.c", 0], # ppc is not done yet (2.0.15) sun3 => '', # sparcv7 => '', # sparcv8 => $sparcv8_kernel, sparcv8_micro => $sparcv8_kernel, sparcv8_super => $sparcv8_kernel, # sparcv9 is not done yet (2.0.15) ); return $level0{$asmarch} || ["$asmarch/level0.s", 0]; } sub kernel_fill_data { my ($kernels, $hash) = (shift, shift); # The original file return unless @$kernels; $hash->{file1} = "\$(PARI_DIR)/src/kernel/$kernels->[0]"; $hash->{convert1} = $kernels->[1]; # The (possibly) preprocessed file $hash->{converted1} = $hash->{convert1} ? 'kernel1.s' : $hash->{file1}; # Extra bookkeeping ($hash->{header1} = $hash->{file1}) =~ s/\.c$/.h/; $hash->{header1} = '' unless -r $hash->{header1}; # Put as a dependence ($hash->{dir1} = $hash->{file1}) =~ s/[^\/]*\.[csS]$//; # Include with -I # Additional file to compile (original and converted) $hash->{file2} = $hash->{converted2} = "\$(PARI_DIR)/src/kernel/$kernels->[2]" if $kernels->[2]; $hash->{file2} = $hash->{converted2} = '' unless $kernels->[2]; $hash->{converted2} = 'kernel2.s' if $kernels->[3]; $hash->{convert} = ($hash->{converted2} eq 'kernel2.s' or $hash->{converted1} eq 'kernel1.s'); if ( $^O =~ /win32/i and $Config{cc} =~ /\bcl/ # M$ VC doesn't understand .s and $hash->{converted1} eq 'kernel1.s' ) { $hash->{converted1} = 'kernel1.c'; } } sub assembler_flags_via { my ($machine, $not_gnu_as) = (shift, shift); my %assf = ( # alpha => "-O1", # Not supported any more sparc => ($Config{osname} eq 'solaris' ? "-P -T -I." : "-P -I."), hppa => "+DA1.1", ); my $assflags = $assf{$machine =~ /sun3|sparc/ ? 'sparc' : $machine} || ''; $assflags .= ' -D__GNUC__' # hiremainder problem with gcc on Solaris if $not_gnu_as and $Config{gccversion}; # Tested with Sun WorkShop 6 update 2 Compiler Common 6.2 Solaris_9_CBE 2001/04/02: $assflags .= ' -K PIC' # check assembler message for hints # disable: leads to segfaults on Solaris if 0 and $not_gnu_as and $Config{cccdlflags} =~ /(^|\s)-K\s*(pic|PIC)\b/ and $not_gnu_as =~ /(^|\W)-K\s+\{?(\w+,)PIC\b/; # in GNU as, default??? return $assflags; } sub assembler_flags { # Backward compatibility my ($machine, $is_gnu_as) = (shift, shift); assembler_flags_via($machine, not $is_gnu_as); } sub extra_includes { my $pari_dir = shift; # Some #include directives assume us inside $pari_dir/OARCH; replace by src return join ' -I ', '', grep -d, "$pari_dir/src/systems/$^O", "$pari_dir/src"; } sub build_funclists_ourselves ($) { my $pari_dir = shift; chdir "$pari_dir/src/desc" or die "Can't chdir to `$pari_dir/src/desc'"; unless (-f 'pari.desc') { my $t = 'tmp-pari.desc'; #warn "Running `$^X merge_822 ../functions/*/* > $t'...\n"; system "$^X merge_822 ../functions/*/* > $t" and die "Can't run `$^X merge_822 ../functions/*/* > $t'"; rename $t, 'pari.desc' or die "rename failed: $t => 'pari.desc'"; } my %recipies; if (-f 'gen_help') { # pre-2.2.13 %recipies = ( 'language/members.h' => [[qw(gen_member)]], 'language/init.h' => [[qw(gen_proto basic)], [qw(gen_help basic)]], 'gp/highlvl.h' => [[qw(gen_proto highlevel)], [qw(gen_help highlevel)]], 'gp/gp_init.h' => [[qw(gen_proto gp)], [qw(gen_help gp)]], ); } else { %recipies = ( 'language/members.h' => [[qw(gen_member)]], 'language/init.h' => [[qw(gen_proto basic)]], 'gp/highlvl.h' => [[qw(gen_proto highlevel)]], 'gp/gp_init.h' => [[qw(gen_proto gp)]], ); } for my $outfile (keys %recipies) { next if -r "../$outfile"; my $append = '>'; for my $step (@{$recipies{$outfile}}) { #warn "Running `$^X @$step pari.desc $append ../$outfile-tmp'...\n"; system "$^X @$step pari.desc $append ../$outfile-tmp" and die "Can't run `$^X @$step pari.desc $append ../$outfile-tmp'"; $append = '>>'; } rename "../$outfile-tmp", "../$outfile" or die "rename failed: ../$outfile-tmp => ../$outfile"; } 1; } sub build_funclists { my $pari_dir = shift; return unless -d "$pari_dir/src/desc"; # Old version, no autogeneration return if -f "$pari_dir/src/language/init.h" and -f "$pari_dir/src/desc/pari.desc"; if (-f "$pari_dir/src/desc/Makefile") { # Old development version # Keeps checksum to update when needed; fake it open FL, "> $pari_dir/src/funclist" and close FL # Ignore errors unless -f "$pari_dir/src/funclist"; (system("cd $pari_dir/src/desc && make") and system("cd $pari_dir/src/desc && make SHELL=cmd") or not -s "$pari_dir/src/desc/pari.desc") and (unlink("$pari_dir/src/desc/pari.desc"), die < code values. =cut sub ep_codes_from_file ($\%\%) { my ($file, $descrh, $names) = (shift, shift, shift); local $_; open IN, "< $file" or warn "Cannot open `$file': $!" and return; while () { next unless /^\s*{\s*\"/; chomp; warn("Unrecognized line: `$_'\n"), next unless /^\s*\{\s*"(\w+)"\s*,\s*(\d+)\s*,[^,]*,\s*\d+\s*,(?:\s*\d+\s*,)?\s*("((?:\\.|[^"])*)"|NULL)\s*(,|\})/; next unless defined $4; #print; my ($name, $code, $descr) = ($1, $2, $4); $descrh->{$code} = [] unless exists $descrh->{$code}; push @{$descrh->{$code}}, $descr unless grep $descr eq $_, @{$descrh->{$code}}; warn "! Duplicate code $code for function '$name' (was $names->{$name})\n" if defined $names->{$name}; $names->{$name} = [$code, $descr]; } close IN or warn "Cannot close `$file': $!"; } =item ep_hash_report(%hash, %names, $fh) Writes to $fh the diagnostic about problemes with the string interface descriptions corresponding to the numeric codes. If $fh is false, returns TRUE if no problem were found. =cut my $expected_codes_as_in = '2.1.3'; # Not in 2.1.3 my ($dummy1, %old_expected_codes) = split /\s+/, qq( 13 GD0,L,D0,G, 34 vLLL ); my ($dummy2, %expected_codes) = split /\s+/, qq( 1 Gp 2 GG 3 GGG 4 GGGG 10 lG 11 L 12 GnP 14 GDn 16 ls 18 G 19 vLL 20 lGG 21 GL 22 GVI 23 GL 24 LG 25 GGD0,L, 26 GnG 27 V=GIp 28 GDVDI 29 GGp 30 lGGG 31 GDGDGD& 32 GGL 33 GGGD0,L,p 35 vLGG 37 V=GGIp 45 LGD0,L, 47 V=GGIDG 48 V=GGIDG 49 GGDVDVDI 57 vLs 59 vLGGGG 62 GD0,G,D0,G,D0,L,p 73 LV=GGIpD0,L,D0,L, 83 vV=GGI 84 vGVI 85 vS 86 vV=GGGI 87 vV=GID0,L, 91 GD0,L,DGp 96 GD0,L,DGp ); # Some historic changes in interfaces we do not care about (E vs I) my $t; my %variations = map {($t = $expected_codes{$_}) =~ s/I/E/ ? ($_, $t) : ()} keys %expected_codes; my %known_unimplemented = (57 => 1, 62 => 1); sub ep_hash_report (\%;\%$) { my ($h, $names, $fh) = (shift, shift, shift); my ($c, @list); $names = {} unless defined $names; my @keys = grep {$_ ne 0 and $_ ne 99} keys %$h; if (@list = grep {not exists $h->{$_}} keys %expected_codes) { return unless $fh; print $fh <{$c}}" EOP my $list = join ", ", grep $names->{$_}[0] == $c, sort keys %$names; print $fh <{$_}} != 1, @keys) { return unless $fh; print $fh <{$c}}" EOP my $list = join ", ", grep $names->{$_}[0] == $c, sort keys %$names; print $fh <{$_}}" ne $expected_codes{$_} and "@{$h->{$_}}" ne $variations{$_} and not $known_unimplemented{$_}} @keys) { return unless $fh; print $fh <{$c}}" (was meaning "$expected_codes{$c}" in $expected_codes_as_in) EOP my $list = join ", ", grep {$names->{$_}[0] == $c and $names->{$_}[1] ne $expected_codes{$c}} sort keys %$names; print $fh <= 2002002) { my $c; for $c (qw(26 62)) { delete $expected_codes{$c}; } } } =item code_C_translator() Returns string for C code to translate code string to the interface number. Due to a bug in C_constant(), need to translate C<''> to 9900 by hand outside of this subroutine. =cut sub code_C_translator { # Some historic changes in interfaces we do not care about (E vs I) my %c = (%old_expected_codes, %expected_codes); my %codes; @codes{values %c} = keys %c; my $k; for $k (keys %codes) { (my $kk = $k) =~ s/I/E/g; $codes{$kk} = $codes{$k} unless exists $codes{$kk}; ($kk = $k) =~ s/D0,G,/DG/g; # New alternative syntax $codes{$kk} = $codes{$k} unless exists $codes{$kk}; } #$codes{''} = 9900; # bug in C_constant - can't handle $codes{'p'} = 0; require ExtUtils::Constant; my @t = ExtUtils::Constant::constant_types(); # macro defs my @tt = ExtUtils::Constant::C_constant( 'Math::Pari::func_type', 'func_ord_by_type', undef, undef, undef, undef, map {{name => $_, value => $codes{$_}, macro => 1}} keys %codes); # 23 == 21 47==48 91 == 96 (this one unsupported) join '', @t, @tt; } =back =cut 1;