package Math::PariBuild; $VERSION = '2.030502'; require Exporter; @ISA = 'Exporter'; @EXPORT = qw(get_pari_version pari_formatted_version find_pari_dir download_pari patch_pari patches_for 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 pari_formatted_version_from_includes ); use strict; use Config; use File::Copy 'copy'; use File::Basename 'basename'; =head1 NAME Math::PariBuild - utility functions used during configuration of C<Math::Pari>. =head1 SYNOPSIS use Math::PariBuild; =head1 DESCRIPTION =over =item C<get_pari_version($dir)> 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 <IN>; close(IN) or die "error closing '$dir/config/version'"; $v =~ s/\.$// or return; return $v; } =item C<pari_formatted_version($dir)> 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(<<EOW); Could not extract version from '$dir/config/version'; Do you remember that paridir should be location of PARI/GP source tree, not the location of compiled+installed files? Trying extract from the directory name... EOW } return sprintf '%d%03d%03d',$1, $2, $3 if $dir =~ /(\d+)\.(\d+).(\d+)(\.(alpha|beta))?$/; warn(<<EOW); Directory `$dir' has unknown syntax... EOW return $dir; } =item find_pari_dir() Returns the GP/PARI build directory, looking for it as a kid, sibling, or parent of the current directory. [Currently skips versions 2.3.* if possible.] =cut my $latmus = 'src/test/in/nfields'; sub filter_versions_too_new { my $force = shift; my @dirs = grep !m((?:^|[\\/])pari-(?:$common::skip_versions)), @_; print "Filtered out versions too new... <@dirs> out of <@_>\n" if @dirs != @_; return @dirs if $force or @dirs; print "Nothing remained, so I ignore the filter...\n" if @_; return @_; # Not found, not forced } sub find_pari_dir { my ($dir, @dirs, @gooddirs); # Try to find alongside for $dir ('.', '..', '../..', '../../..') { @dirs = filter_versions_too_new 0, grep -d, <$dir/pari-[234].*>; @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 { <<EOP; One can rerun Makefile.PL after fetching GP/PARI archive (e.g., pari-2.1.7.tgz, or pari-2.3.4.tar.gz) manually to the current directory, or a (grand)parent directory of the current directory. [Keep in mind that the numbers "inside version" of Math::Pari module correspond to the last versions of GP/PARI it was tested with (additionally, 2.0108* works best with the last 2.1.* version, 2.1.7). As an alternative to having archive in CWD or its (grand)parent, specify pari_tgz=PATH_TO_TAR_GZ option to Makefile.PL. There is no need to extract the archive, or build GP/PARI; but if you have it extracted [and patched, if needed], you may specify paridir=PATH_TO_DIST_DIR option to Makefile.PL instead of `pari_tgz'. However, in this case the files WON'T be auto-patched (unless it is in a subdir, or force_patching). As a last-resort solution, there is also a possibility to use an already compiled PARI library. See the documentation in README and INSTALL files.] EOP } sub debug_no_response ($) { return '' unless $ENV{AUTOMATED_TESTING}; my $r = shift; my $c = $r->content; my $t = $r->content_type; $c = '<<undef>>' 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/2.1 dir binary get pari-2.1.7.tgz quit EOF close OF or die "Can't close `ftp-cmd' for write: $!"; print <<EOP; ============================================== ftp -pinegv megrez.math.u-bordeaux.fr < ftp-cmd EOP my $rc = system "ftp -pinegv megrez.math.u-bordeaux.fr < ftp-cmd"; print <<EOP; ============================================== EOP return if $rc; # XXXX Temporarily disable continuing build (to see smoke testing reports) warn <<EOW; ========================================================================== === === If the output from FTP session above successfully lists === directories, your installs of Net::FTP and, possibly, LWP === is completely broken. How comes??? === ========================================================================== EOW warn(<<'EOW'), return unless $ENV{MATHPARI_USEFTP}; FTP session is for debugging only; I'm ignoring the downloaded file. (Set $ENV{MATHPARI_USEFTP} to TRUE to actually use the downloaded file.) EOW return 'pari-2.1.7.tgz'; } sub extract_pari_archive ($) { my $file = shift; 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" }; (my $dir = $file) =~ s,(?:.*[\\/])?(.*)\.t(ar\.)?gz$,$1, or die "malformed name `$file'"; -d $dir or die "Did not find directory $dir!"; return $dir; } sub fmt_version {sprintf "%03d%03d%03d", split /\./, shift} sub pari_formatted_version_from_includes($) { # Unsupported; to enable parilib option; based on my $prefix = shift; # http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/pkgsrc/math/p5-Math-Pari/patches/patch-aa open my $fh, "<", "$prefix/pari/paricfg.h" or die $!; while( my $line = <$fh> ) { next unless $line =~ m/^#define\s+PARIVERSION.*(\d+\.\d+\.\d+)/; return fmt_version $1; } } sub finish_download_pari ($$$$;$) { my($base_url, $dir, $_archive, $ftp, $ua) = (shift, shift, shift, shift, shift); my %archive = %$_archive; 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 (my $size = -s $file) { print qq(Well, I already have it (size=$size), 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); } $dir = extract_pari_archive($file); } return ($dir, $version); } sub download_pari { my ($srcfile, $force) = (shift, shift); my $host = 'megrez.math.u-bordeaux.fr'; my $dir = '/pub/pari/'; my($ftp, $ua, $base_url); print "Did not find GP/PARI build directory around.\n" unless defined $srcfile; my @match = ( '((?:.*\/)?pari\W*', '(\d+\.\d+\.\d+).*\.t(?:ar\.)?gz)$' ); my $match_all = "$match[0]$match[1]"; my $match = "$match[0](?!$common::skip_versions)$match[1]"; my %archive; my $match_pari_archive = sub { my ($file, $ok23) = (shift, shift); return unless $ok23 ? $file =~ /$match_all/o : $file =~ /$match/o; $file = $1; my $version = $2; if ($file =~ /alpha/) { $archive{alpha}{$version} = $file; } elsif ($file =~ /beta/) { $archive{beta}{$version} = $file; } else { $archive{golden}{$version} = $file; } }; if ($srcfile and -s $srcfile) { die "The FILE supplied via the pari_tgz=$srcfile option did not match /$match/" unless $match_pari_archive->($srcfile, 'ok2.3'); return finish_download_pari($base_url, $dir, \%archive, undef, $ua); } else { if ($force) { print "Forced autofetching...\n\n" } elsif ($^O =~ /^MSWin32\b/ and $Config{ptrsize} == 8 and $Config{longsize} == 4) { print <<EOP; Apparently, you are running a 64-bit Perl built with MicroSoft's compilers. GP/PARI (at least the versions I know how to work with, 2.1.* and 2.3.*) cannot be built in this environment. I won't auto-download GP/PARI. If you believe that this message is printed erroneously, please report (see files README and INSTALL), and put force_download on the command line: perl Makefile.PL force_download EOP print manual_download_instructions(); return; } 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 = <<EOP; Do you want to me to fetch GP/PARI automatically? (If you do not, you will need to fetch it manually, and/or direct me to the directory with GP/PARI source via the command-line option paridir=/dir) Make sure you have a large scrollback buffer to see the messages, or `tee' the STDOUT/STDERR to a file. Fetch? (y/n, press Enter) EOP chomp $mess; print "$mess "; my $ans = <STDIN>; if ($ans !~ /y/i) { if ($ans !~ /[^\n\r]/ and not $ENV{PERL_MATHPARI_TRUST_MANUAL} and (defined $ENV{PERL5_CPAN_IS_RUNNING} or ($ENV{PERL_EXTUTILS_AUTOINSTALL}||0) =~ /\bdefaultdeps\b/) ) { print <<'EOP'; Hmm, did not you read the prompt? Anyway, since $ENV{PERL5_CPAN_IS_RUNNING} is set (or $ENV{PERL_EXTUTILS_AUTOINSTALL} contains defaultdeps), I assume unattended build, and consider NO ANSWER as agreement... (If this is not what you wanted, set PERL_MATHPARI_TRUST_MANUAL to TRUE.) EOP $ans = 'y'; } else { print <<EOP; Well, as you wish... I won't download it automatically... EOP } my ($eA, $eM, $eC, $eE, $tI, $tO, $tE, $pO, $pE) = (@ENV{ qw(AUTOMATED_TESTING PERL_MM_USE_DEFAULT PERL5_CPAN_IS_RUNNING PERL_EXTUTILS_AUTOINSTALL) }, -t STDIN, -t STDOUT, -t STDERR, -p STDERR, -p STDOUT); defined() ? $_ = "'$_'" : $_ = '<undef>' for $eA, $eM, $eC, $eE; my @ans = map {sprintf '%#x', ord} split //, $ans; print <<EOP; # Is AUTOMATED_TESTING ALWAYS defined on smoke??? [ to debug Smoke Tests: AUTOMATED_TESTING=$eA PERL_MM_USE_DEFAULT=$eM ans=@ans PERL5_CPAN_IS_RUNNING=$eC PERL_EXTUTILS_AUTOINSTALL=$eE -t STDIN/STDOUT/ERR = $tI/$tO/$tE -p STDOUT/ERR = $pO/$pE ] EOP unless ($ans eq 'y') { print manual_download_instructions(); return; } } } else { print "Non-interactive session, autofetching...\n\n" } $base_url = "ftp://$host$dir"; my @extra_chdir = qw(OLD/2.3 OLD/2.1 OLD); print "Getting GP/PARI from $base_url\n"; my @ret = eval { die "This is not an FTP url: $base_url" unless $base_url =~ m(^ftp://); require Net::FTP; $ftp = Net::FTP->new($host) or die "Cannot create FTP object: $!"; $ftp->login("anonymous","Math::Pari@") or die "Cannot login anonymously (",$ftp->message(),"): $!"; my($c, $sub_old) = 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"; # print "skip = /PREFIX($common::skip_versions)/\n"; %archive = (); for my $file (@lst) { $c++ if $match_pari_archive->($file); } $sub_old++ if $dir =~ m(OLD/); unless ($c) { die "Did not find any file matching /$match/ via FTP\n\n" . manual_download_instructions() unless @Extra; $dir = shift @Extra; $dir =~ s(OLD)(..) if $sub_old; print "Not in this directory, now chdir('$dir')...\n"; } } return finish_download_pari($base_url, $dir, \%archive, $ftp) }; return @ret if @ret; die "Panic: unreachable" unless $@; { 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, $do) = 0; # my @Extra = @extra_chdir; my @url = map "$base_url$_", '', map "$_/", @extra_chdir; push @url, map { (my $in = $_) =~ s(^ftp://)(http://); $in } @url; while (not $c) { $base_url = shift @url; print "Not in this directory, trying `$base_url'...\n" if $do++; $ua = LWP::UserAgent->new; $ua->env_proxy; my $req = HTTP::Request->new(GET => $base_url); my $resp = $ua->request($req); $resp->is_success or warn("Can't fetch directory listing from $base_url: " . $resp->as_string), next; %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 (@url) { warn debug_no_response($resp) . "Did not find any file matching /$match/ via FTP/HTTP.\n\n"; my $f = ll_ftp or die manual_download_instructions(); return download_pari($f); } # my $dir = shift @Extra; } } } return finish_download_pari($base_url, $dir, \%archive, undef, $ua) } } =item C<patches_for($version)> 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 =~ /^MSWin32\b/ ? '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 =~ /^MSWin32\b/ ? 'patches/diff_2.3.5_mingw-w64' : ()), 'patches/diff_2.3.5_stderr_clobber'], ); 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'; push @p, qw( patches/diff_add_gnuplotNeeded patches/diff_add_gnuplotAdd ) if $v =~ /^2\.3\b/; # ge '2.2.13'; @p; } =item C<patch_pari($dir [, $version])> 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 (push @common::patches_fail, $p), warn "...Could not patch: \$?=$?, $!; continuing anyway...\n"; $rc .= "'$pp' => $?, " } print "Finished patching...\n"; $common::patches_run = 1; if (open my $f, ">> $dir/.perl.patches") { print $f join "\n", "# Needed patches:", @patches, "# Failed patches (if any):", @common::patches_fail, ''; close $f; } else { warn "??? Cannot report which patches were applied in $dir/.perl.patches: $!" } $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<make_pod($podfile, $gphelp_opt, $dir)> 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<gphelp>. =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 -I. 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 (<INC>) { $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<build_tests($dir)> 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<find_paricfg($dir)> Finds suitable (?) files F<paricfg.h> 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 =~ /^MSWin32\b/ and -f "$paridir/win32/paricfg.h"; @paricfg; } =item C<find_paricfg($dir, $do_configure)> Finds suitable (?) files F<paricfg.h> in GP/PARI build directory $dir. If $do_configure is true, runs GP/PARI's Configure script to build one. Returns FALSE if F<paricfg.h> 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 <<EOW; Did not find paricfg.h. You may need to manually copy it to libPARI directory from the GP/PARI build directory. ...Now switching to creation of paricfg.h by Perl code. EOW return 0; } my $found = $paricfg[0]; if (@paricfg > 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 <<EOP; # Duplication with build_paricfg()... Existing libPARI/paricfg.h not older than $found. ...Will not overwrite libPARI/paricfg.h... (remove it manually if needed); You may also want to remove libPARI/paricfg.h if you configuration changed from the time of the first build in this directory... EOP } else { print "Found $found, copying it to libPARI...\n"; copy $found, 'libPARI/paricfg.h' or die "Could not copy $found to paricfg.h: $!" if not -e 'libPARI/paricfg.h' or -M $found < -M 'libPARI/paricfg.h'; } return 1; } =item write_paricfg($formatted_version) Writes PARI configuration file F<libPARI/paricfg.h>. 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 unless $^O =~ /win32/i; # Should not we check for CygWin? #define UNIX EOP my $shellq = ($^O eq 'os2' or $^O =~ /win32/i or $^O eq 'dos') ? q(") : q('); my $datadir = '/usr/local/lib/pari/'; warn "Trying to find DATADIR of an installed version of GP/PARI (if such exists)...\n"; { local %ENV; delete $ENV{GP_DATA_DIR}; (my $o = `$^X -wle "print shift" "print(default(datadir))" | gp -q` || '') =~ s,/?\n*$,,; $datadir = "$o/" if length $o and -d $o; } # with 2.1.7, it is either in one of $miscdir/galdata $share_prefix/pari/galdata my $miscdir = $datadir; # Not needed with 2.3.4 $datadir .= "galdata/" if $version < 2003000; print F <<EOP; #define SHELL_Q '\\$shellq' EOP print F <<EOP; #define GPDATADIR "$datadir" #define GPMISCDIR "$miscdir" #define PARI_BYTE_ORDER $Config{byteorder} #define NOEXP2 /* Otherwise elliptic.t:11 rounds differetly, and fails */ EOP if ($opts{have_getrusage}) { print F <<EOP if $Config{d_times}; #define USE_GETRUSAGE 1 EOP } else { print F <<EOP if $Config{d_times} and $^O !~ /win32/i; # times() missing there... #define USE_TIMES 1 EOP print F <<EOP if $Config{d_times} and $Config{i_time} and !$opts{clk_tck_def}; /* Reported to be needed on some Linuxes: */ #include <time.h> EOP print F <<EOP if not $Config{d_times} and $Config{d_ftime}; #define USE_FTIME 1 EOP } print F <<EOP if $Config{dlsrc} eq 'dl_dlopen.xs'; #define HAS_DLOPEN EOP print F <<EOP unless $opts{have_ulong}; #define ULONG_NOT_DEFINED EOP my $arch = find_machine_architecture(); my $bits64 = ($arch =~ /alpha|64/ # ppc is 32bit or defined($Config{longsize}) and $Config{longsize} == 8); print F <<EOP if $bits64; #define LONG_IS_64BIT 1 EOP if (!$bits64) { # Order of words in a double my @w = unpack 'LL', pack 'd', 2; my $f = $w[1] ? 1 : 0; die "Unknown double format" unless $w[$f] == (1<<30) and $w[1-$f] == 0; print F <<EOP; #define PARI_DOUBLE_FORMAT $f EOP } print F <<EOP; #define DL_DFLT_NAME NULL EOP print F <<EOP if $arch eq 'port'; #define __HAS_NO_ASM__ EOP my @v = ($version =~ /^(\d+)(\d{3})(\d{3})$/) or die "write_paricfg() needs a formatted version argument, got `$version'"; my $vvv = ((($v[0]) << 16) + (($v[1]) << 8) + ($v[2])); print F <<EOP; # <=2.3.0 it was used only in gp.c, which we don't compile #define PARI_VERSION_CODE $vvv #define PARI_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #define PARI_VERSION_SHIFT 8 EOP my($V1,$V2,$V3) = $version =~ /(\d+?(?=\d{6}$))0*(\d+?(?=\d{3}$))0*(\d+)$/ or die "Unexpected format of version=$version"; my $rel = ($V2 & 1) ? 'released' : 'experimental'; my $bits = $bits64 ? 64 : 32; print F <<EOP if $version >= 2004000; #define PARIVERSION \"GP/PARI CALCULATOR Version $V1.$V2.$V3 ($rel) configured by Math::Pari\" #define PARIINFO \"PerlArch=$^O ($arch kernel); $bits-bit version\" #define PARI_VCSVERSION \"\" EOP close F or die "close 'libPARI/paricfg.h' for write: $!"; %opts; } =item C<build_paricfg($dir, $do_configure)> Builds F<libPARI/paricfg.h> 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 <<EOP unless $do_configure; # Duplication with find_or_Configure_paricfg() ...Will not overwrite libPARI/paricfg.h... (remove it manually if needed) You may also want to remove libPARI/paricfg.h if your configuration changed from the time of the first build in this directory... EOP } else { print "...Generating libPARI/paricfg.h ...\n"; %opts = write_paricfg($version); } } %opts; } # The following two functions are based on the logic in the PARI # Configure script (updated to 2.3.0's config/arch-osname): sub process_sparc { my $info = shift; # *SuperSparc*) arch=sparcv8_super;; # *TMS390Z5[05]*) arch=sparcv8_super;; # SuperSparc I or II # *MB86934*) arch=sparcv8_super;; # SparcLite # *RT625*) arch=sparcv8_super;; # HyperSparc # *CY605*) arch=sparcv8_super;; return 'sparcv8_super' if $info =~ /SuperSparc|TMS390Z5[05]|CY605|MB86934|RT625/; # *TMS390S1[05]*) arch=sparcv8_micro;; # MicroSparc I # *MB86904*) arch=sparcv8_micro;; # MicroSparc II # *MB86907*) arch=sparcv8_micro;; # TurboSparc return 'sparcv8_micro' if $info =~ /TMS390S1[05]|MB8690[47]/; return shift; } =item find_machine_architecture() Returns the type of the processor of the current machine. =cut sub find_machine_architecture () { my $os = (split ' ', $Config{myuname})[0]; my $machine = $os; # Handles fx2800 if ($os =~ /^irix/) { $machine = 'irix'; } elsif ($os =~ /^hp/) { $machine = `uname -m` || 'hppa'; if ($machine =~ m(^9000/[34])) { $machine = 'm68k'; } elsif ($machine ne 'ia64') { $machine = 'hppa'; } } elsif ($os eq 'os2' or $os eq 'netbsd' or $os eq 'freebsd' or $os =~ /^cygwin/) { chomp($machine = `uname -m`); $machine ||= 'ix86'; } elsif (($Config{archname} =~ /^MSWin32-x86\b/) && ($Config{cc} =~ /gcc/) and ($Config{longsize} == 4)) { $machine = 'ix86'; } elsif (0 and $os =~ /win32/i and not $Config{gccversion}) { # Not needed with rename of kernel1.s to kernel1.c? $machine = 'port'; # Win32 compilers would not understand the assmebler anyway } elsif ($os eq 'ultrix') { $machine = 'mips'; } elsif ($os eq 'nextstep' or -d '/NextApps') { chomp($machine = `file /bin/sh | sed 's/.*(for architecture \(.*\))/\1/'`); } elsif ($os eq 'darwin') { chomp($machine = `uname -p`); $machine = 'ppc' if $machine eq 'powerpc'; } elsif ($os eq 'osf1') { $machine = 'alpha' if (split ' ', $Config{myuname})[4] eq 'alpha'; } elsif ($os =~ /^cygwin/) { $machine = $ENV{HOSTTYPE}; } elsif ($os eq 'linux') { chomp($machine = `uname -m`); $machine = 'sparcv9' if $machine eq 'sparc64'; $machine = 'hppa' if $machine =~ /^parisc/; if ($machine eq 'sparc' and -e '/proc/cpuinfo' && -R '/proc/cpuinfo') { open IN, '/proc/cpuinfo' or die "open /proc/cpuinfo: $!"; local $/ = undef; # Needed? my $info = <IN>; 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/\s+$//; # trailing \cM on cygwin ??? } $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 = <ASS>; close ASS; unless ($assout) { eval {alarm 10}; # Be extra safe... open ASS, "$ass -v 2>&1 $devnul |"; $assout = <ASS>; 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 (<F>) { 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 <<EOW; #### Do not know how to build for assembler `$asmarch'. #### #### Reversing to assembler-less type `port'. #### #### #### #### If you think your processor's assembler is supported #### #### by PARI, edit libPARI/Makefile.PL and report. #### #### #### #### Alternatively, specify machine=YOURTYPE or machine=none #### #### on the #### #### perl Makefile.PL #### #### command line. #### #### Recognized types: #### #### alpha hppa m86k none sparcv7 sparcv8 sparcv8_micro #### #### sparcv8_super ix86 (ppc ia64 after 2.2.7) #### EOW $machine = 'port'; $asmarch = 'none'; } if ($asmarch eq 'none') { print "...I will use portable assembler-less build\n"; } else { print "...I will use assembler build of type '$asmarch'.\n"; } print <<EOP if $asmarch =~ /^hppa/; ### ### Some time ago HPPA assembler files were not relocatable, ### if this is still true, they are probably unsuitable for dynamic linking. ### It is advisable to restart Makefile.PL with an extra argument ### machine=port ### if you are planning for dynamic linking of Math::Pari. ### ### NOTE: machine=port results in a significant drop in performance. ### For a static build (which makes a new perl executable with the library ### compiled in [and arranges for it to be compiled in when ### other extensions are statically built later]): ### perl Makefile.PL LINKTYPE=static ### make static ### make perl ### make test ### make install ### EOP return $asmarch; } # Output (last two optional): # [Which file to compile, whether you need to preprocess it to ./kernel1.s, # Additional file to compile, need? to preprocess it to ./kernel2.s,] sub sparcv8_kernel_files_old { 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)$/; my $sparcv8_kernel = ($cvt ? ["sparcv8/level0.$_ext", 1, "sparcv8/level0_$asmarch.$_ext", 1] : ["sparcv8/level0.$_ext", 0, "sparcv8/level0_$asmarch.$_ext", 0]); # kernel2.o is not needed if the compiler can inline assembler: my $sparcv8_need_kernel2 = !$Config{gccversion} || $Config{osname} =~ /^(linux|nextstep)$/; $sparcv8_kernel = [$sparcv8_kernel->[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 -I. merge_822 ../functions/*/* > $t'...\n"; if (system "$^X -I. merge_822 ../functions/*/* > $t") { # On AIX, this exceeds max command line length unlink ($t); warn <<EOW; Can't run `$^X -I. merge_822 ../functions/*/* > $t' Running merge_822 separately in subdirectories... EOW foreach (glob ("../functions/*")) { next unless (-d $_); my @l = glob "$_/*" or next; system "$^X -I. merge_822 @l >> $t" and die "Can't run `$^X -I. merge_822 @l >> $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 -I. @$step pari.desc $append ../$outfile-tmp'...\n"; system "$^X -I. @$step pari.desc $append ../$outfile-tmp" and die "Can't run `$^X -I. @$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 <<EOW); ### ### Apparently, we failed to build function descriptions of GP/PARI. ### Try editing $pari_dir/src/desc/Makefile - a typical reason ### is a wrong value of SHELL for your system. You can run make in ### $pari_dir/src/desc manually too... EOW } else { require Cwd; my $cwd = Cwd::cwd(); my $res = eval { build_funclists_ourselves $pari_dir }; chdir $cwd; die <<EOD unless $res; $@ ### ### We do not know how to build function descriptions of GP/PARI. ### Please build them manually (e.g., by building GP/PARI). ### EOD } } =item ep_codes_from_file($filename,%hash,%names) Adds to the %hash the string interface descriptions corresponding to the numeric codes use in the file's entree array. %hash is indexed by by the numeric codes; the value are references to arrays with the corresponding string interface descriptions. Adds to %names the list of name => 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 (<IN>) { 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 <<EOP; Cosmetic only: the following numeric interfaces are not used any more: EOP for $c (sort @list) { print $fh <<EOP; $c (was meaning "$expected_codes{$c}" in $expected_codes_as_in) EOP } } if (@list = grep {not exists $expected_codes{$_}} @keys) { return unless $fh; print $fh <<EOP; Harmless: the following numeric interfaces are new: EOP for $c (sort @list) { print $fh <<EOP; $c meaning "@{$h->{$c}}" EOP my $list = join ", ", grep $names->{$_}[0] == $c, sort keys %$names; print $fh <<EOP; (appears for $list) EOP } } if (@list = grep @{$h->{$_}} != 1, @keys) { return unless $fh; print $fh <<EOP; May be harmless: non-unique string interfaces for numeric interfaces: EOP for $c (sort @list) { print $fh <<EOP; $c meaning "@{$h->{$c}}" EOP my $list = join ", ", grep $names->{$_}[0] == $c, sort keys %$names; print $fh <<EOP; (appears for $list) EOP } } if (@list = grep {exists $expected_codes{$_} and "@{$h->{$_}}" ne $expected_codes{$_} and "@{$h->{$_}}" ne $variations{$_} and not $known_unimplemented{$_}} @keys) { return unless $fh; print $fh <<EOP; Possible problems with Math::Pari interface to GP/PARI: unexpected value of string interfaces for numeric interfaces: EOP for $c (sort @list) { print $fh <<EOP; $c meaning "@{$h->{$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 <<EOP; (may affect functions $list) EOP } } return 1; } =item ep_in_version($version) Updates the list of codes for the given version of GP/PARI (formatted as in 2002002). =cut sub ep_in_version ($) { my $v = shift; if ($v >= 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; } sub code_C_comment_interface { # Some historic changes in interfaces we do not care about (E vs I) my %c = (%old_expected_codes, %expected_codes); my $res; # s{^(interface(\d+)\(.*)}{ my $res = defined $c{$2} ? qq(\t/* Code="$c{$2}" */) : ''; $1 . $res}e; s[^(interface(\d+)\(.*?\n\s*CODE:\s*\{)]{ my $res = defined $c{$2} ? qq(\t/* Code="$c{$2}" */) : ''; $1 . $res}msge; } =back =cut 1;