The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use ExtUtils::MakeMaker;
use Config '%Config';

#BEGIN {unshift @INC, 'utils'}
use Math::PariBuild;
use strict;

ExtUtils::MakeMaker::WriteEmptyMakefile(), return 1 if $common::parilib;

*MY::top_targets  = \&xMY::top_targets;		# will break WriteEmptyMakefile
*MY::const_config = \&xMY::const_config;

my $main_paridir = $common::main_paridir;
die "Please start Makefile.PL at toplevel, not in libPARI"
  unless $main_paridir;

if ($main_paridir =~ m,^([a-z]:)?[/\\],i) {
  $main_paridir = $common::main_paridir;
} else {
  $main_paridir = "../$common::main_paridir";
}

my $pari_version = $common::pari_version;
my $machine = $common::machine;

if (defined $machine) {
  print "...Via command-line: processor family `$machine'\n";
} else {
  $machine = find_machine_architecture;
}
if ( $machine eq 'port'			# Set by find_machine_architecture()
     and defined($Config{longsize}) and $Config{longsize} == 8 ) {
  #$mycflags .= " -DLONG_IS_64BIT";	# Moved to BuildPari...
}

# Choose which assembler files to use
my $asmarch = choose_and_report_assembler($machine, $pari_version);
# $machine used as a coarse-grain factor below, $asmarch as a fine-grain
$machine = 'port' if $asmarch eq 'none';

# This part is based on analysing Makefile.SH in */kernel/* subdirectories.

my $asscmd;
if ($machine =~ /sparc|alpha|hppa/) {
  $asscmd = '$(AS) $(ASFLAGS)';	#  $(CCCDLFLAGS)?  sparc is non-relocatable...
} else {
  $asscmd = '$(CCCMD) $(CCCDLFLAGS)';
}

my $as_message = not_gnu_as();
my $Using_gnu_as = not $as_message;
print STDERR
  "...Assembler is " . ($Using_gnu_as ? "" : "not ") . "GNU assembler\n";

my $asmcpp_define = '';
if ($Using_gnu_as and $^O eq 'solaris' and $asmarch =~ /^sparc/) {
  $asmcpp_define = '-DNO_UNDERSCORE';
  $asmcpp_define .= ' -D__GNUC__' if $Config{gcc};
}

# Which files to compile and how to build levels1/2 kernel
my $kernels = kernel_files($asmarch, $pari_version, $Using_gnu_as, $main_paridir);
kernel_fill_data($kernels, \ my %kernel);

# remaining kernel C files
my $kernel_dir = '$(PARI_DIR)/src/kernel';
my $mp	       = "$kernel_dir/none/mp.c";
my $mpinl      = "$kernel_dir/none/level1.c"; # Non-inlined versions of inlines
my $mpinlh     = "$kernel_dir/none/level1.h"; # Put as a dependence
if ($pari_version >= 2003000) {
  $mpinl = "$kernel_dir/none/mpinl.c";
  $mpinlh = '';
}


my @cfiles =			# Remaining C files
  grep !m(/ ( plot (?!port|gnuplot)
	    | ix86 | version | mpin | dummy
	    | gp_(rl|init) | kerntest | whatnow | gp\. )
	 )x,	<$main_paridir/src/*/*.c>, # kernel C files deeper in the tree
		<$main_paridir/src/systems/$^O/*.c>;
push @cfiles, "$main_paridir/src/graph/plotnull.c"
  unless -f "$main_paridir/src/graph/plotgnuplot.c";
my @cat_cfiles =			# Remaining kernel C files
  grep !m(/ ( level \d+ | mp ) )x,  <$main_paridir/src/kernel/none/*.c>;


@cfiles = grep ! m(/test/[^/]+\.c$), @cfiles;
map s/^\Q$main_paridir/\$(PARI_DIR)/, @cfiles;
my %cfiles = map {m,/([^/.]*)\.c$,i; ($1,$_)} @cfiles; # By basename

my $noexp2 = '';		# Not used any more - but would be nice!
my $libs="-lm";
my $mycflags = '';

if ($Config{osname} eq 'solaris') {
#  @sc_dirs = '/opt/SUNWspro/lib' if -d '/opt/SUNWspro/lib';
#  @sc_dirs = </opt/SUNWspro/SC*/lib> unless @sc_dirs;
  #warn "Cannot find SUNWspro dirs, needed for -lsunmath, using NOEXP2.\n"
  #  unless @sc_dirs;
#  if (@sc_dirs) {
#    $libs .= " -L$sc_dirs[-1] -lsunmath";
#  } else {
    $noexp2 = 1;
#  }
} elsif ($Config{osname} eq 'sunos') {
  # $machine = 'sparcv8_super' if $Config{myuname} =~ /\bsun4m\b/o;
  $mycflags .= ' -DULONG_NOT_DEFINED';
}

# We remove optimize options, since Perl probably knows them
# and one can specify "OPTIMIZE=-O2 -m486" on the command line

my $extra_inc = extra_includes($main_paridir);

my $inc = '';
if ($Config{cc} eq 'gcc') {
  # We do not put -ansi to avoid versioncflags
  $mycflags="";			#$mycflags="-O2 -g";
  if ($Config{osname} eq 'os2' and not $extra_inc =~ /os2(?!\S)/) {
    # 2.1.3 does not include dlctl.h
    $mycflags .= ' -DRTLD_LAZY=0 -DRTLD_GLOBAL=0';
    $inc .= ' -I $(PERL_INC)';
  }
} elsif ($machine eq 'hppa') {
  $mycflags="-Aa -DHPPA";	#$mycflags="-O -Aa -DHPPA";
} elsif ($machine eq 'ix86') {
  $mycflags="";			#$mycflags="-O2 -m486";
} else {
  $mycflags="";			#$mycflags="-O";
}

# These are not needed with newer versions of PARI
# $mycflags .= ' -Dshifts=pari_shifts'; # Conflict with libnsl

$mycflags .= ' -Derr=pari_err'; # On linux it can get a wrong dynamic loading
my $add_ar_flags = '';

if ($machine eq 'alpha') {
  #$mycflags .= " -DLONG_IS_64BIT";	# Moved to BuildPari...
} elsif ($Config{osname} eq 'solaris') {
  $mycflags .= " -DSOLARIS";
} elsif ($Config{osname} eq 'os2') {
  $noexp2 = 1;
  $mycflags .= ' -DMALLOC_PROCS -DHAS_STRICMP';
  $add_ar_flags = '-p64';	# Need big library. 32 is OK without debugging
} elsif ($Config{osname} eq 'linux') {
  $noexp2 = 1;
}

# Since Perl's Config has no direct checks for stat() and opendir(), use presence of OpenGroup's header files
$mycflags .= ' -DHAS_STAT'	if $Config{i_sysstat};
$mycflags .= ' -DHAS_OPENDIR'	if $Config{i_dirent};

if ($Config{gccversion}
    and not $common::do_configure
    and not ($asmarch eq 'alpha'
	     and $Config{gccversion} !~ /^(3\.)|(2\.95\.3.*)/)) {
  $mycflags .= " -DASMINLINE";
}
$mycflags .= ' -DGCC_INLINE' if $Config{gccversion};

my @obj_files = map { "$_\$(OBJ_EXT)" } keys(%cfiles), qw(mp mpinl);
push @obj_files, 'kernel$(OBJ_EXT)' if $kernel{converted1};
push @obj_files, 'kernel2$(OBJ_EXT)' if $kernel{converted2};

$mycflags .= " -DDYNAMIC_PLOTTING";
# OMF build needs no underscores:
$mycflags .= " -D__NO_AOUT" if $^O eq 'os2' and !$OS2::is_aout;

# This is done in Makefile in GP/PARI, and in paricfg.h without do_configure
$mycflags .= ' -DDL_DFLT_NAME=NULL' if $common::do_configure;

# Fix for broken gcc 2.95 with -ffast-math
my @opt;
my $opt = $Config{optimize};

if (defined $Config{gccversion} and $Config{gccversion} =~ /^2\.9[46]/
    and $opt =~ s/-ffast-math// and not "@ARGV" =~ /\bOPTIMIZE=/) {
  @opt = ( OPTIMIZE => $opt );
}

build_funclists($main_paridir);


my $Using_ms_vc = ($^O =~ /win32/i and $Config{cc} =~ /cl/i);
my $Using_Borland = ($^O =~ /win32/i and $Config{cc} =~ /\bbcc/i);

WriteMakefile(
    NAME	=> 'Math::PARI::libPARI',
    LINKTYPE	=> 'static',
    LIBS	=> $libs,
    OBJECT	=> join(' ', @obj_files),
    @opt,
    macro	=> {
		    PARI_DIR	  => $main_paridir,
		    ADD_AR_OPT	  => $add_ar_flags,
		    CPP		  => $Config{cpprun},		# May be a reason for problems with _hiremainer _overflow
		    # CPPMINUS	  => $Config{cppminus},
		    ASSCMD	  => $asscmd,
				# Extra defines when CPPing assembler files:
		    ASSCPPDEFINE  => $asmcpp_define,
				# Use this when assembling:
		    ASFLAGS	  => assembler_flags_via($machine, $as_message),
				# defines used when assembling:
		    ASSDEFINE	  => ($Using_gnu_as ? '' : '$(DEFINE)'),
		    MY_CC_PRE_TARGET => ($Using_ms_vc
					 ? '-Fo'
					 : ($Using_Borland
					    ? '-o': '-o $(MY_EMPTY_STR)')),
		    MY_AR_PRE_TARGET => ($Using_ms_vc
					 ? '-out:'
					 : ($Using_Borland
					    ? '' : 'cr $(MY_EMPTY_STR)')),
		    MY_AR_OBJECT => ($Using_Borland
				     ? '$(OBJECT:^"+")'
				      : '$(OBJECT)'),
		    MY_EMPTY_STR  => '', # The rest to simplify remote debug:
		    USED_ASMARCH  => $asmarch,
		    CFG_CCCDLFLAGS => $Config{cccdlflags},
		   },
    DEFINE	=> $mycflags,
    INC		=> $extra_inc . ' -I $(PARI_DIR)/src/headers -I $(PARI_DIR)/src/graph -I .'
			. $inc,
    C		=> \@cfiles,
    SKIP	=> [qw( distclean test dist makeaperl xs_o static)],
    clean	=> {FILES =>
		    'libPARI$(LIB_EXT) pariinl.h kernel1.s kernel2.s paricfg.h mp.c kernel1.c'},
);

# Create preprocessed copies of assembler files (GNU as does not run cpp);
# Create .h file with inlining rules and an object file mpinl with
# non-inlined versions of normally inlined functions.
# Assemble assembler files.
# Compile C files from a *different* directory.
# Create a library;
sub xMY::top_targets {
  my $converted = '';
  my @inlines2 = inline_headers2($asmarch, $pari_version, $main_paridir);
  my $inlines_cond = $inlines2[0];
  my $inlines = $inlines2[1];
  $converted = <<EOC if $kernel{convert};
$kernel{converted1}: $kernel{file1} $kernel{header1}
	\$(CPP) -I . \$(INC) -I $kernel{dir1} \$(DEFINE) \$(ASSCPPDEFINE) $kernel{file1} | perl -ne "s/%\\s+/%/g; print unless /^\\s*#/" > \$@

kernel2.s: $kernel{file2}
	\$(CPP) -I . \$(INC) -I $kernel{dir1} \$(DEFINE) \$(ASSCPPDEFINE) $kernel{file2} | perl -ne "s/%\\s+/%/g; print unless /^\\s*#/" > \$@

EOC
  my $create_mp = '';
  if ($pari_version >= 2002005) {
    my $mp_pre = $mp;
    $mp = 'mp.c';
    my $kern = "$kernel_dir/none";
    my @dep			# Copied from src/kernel/none/MakeLVL1.SH 2.2.10
      = "$kern/mp.c $kern/cmp.c $kern/gcdll.c $kern/ratlift.c $kern/gcd.c $kern/invmod.c $kern/mp_indep.c $kern/add.c";
    $create_mp = <<EOS;
mp.c: @dep
	\$(PERL) -pe1 @dep > \$@
EOS
  }
  my $prepend_inl = "\t\$(PERL) -wle0 > \$@\n"; # So that `>>' works...
  if (@$inlines_cond) {
    $prepend_inl = <<EOI;
	\$(PERL) -wle "print q(#ifndef ASMINLINE)" > \$@
	\$(PERL) -pe1 @$inlines_cond		  >> \$@
	\$(PERL) -wle "print q(#endif)"		  >> \$@
EOI
  }
  return '
all :: libPARI$(LIB_EXT)

#  Makefile.PL skips sections: distclean test dist makeaperl xs_o static
#  We need to restore targets which may be called from outside (e.g., 5.22 starts to call test_dynamic)

static ::       libPARI$(LIB_EXT)


test:

test_:

test_static:

test_dynamic:

testdb:

testdb_static:

testdb_dynamic:


libPARI$(LIB_EXT): $(OBJECT)
	-$(RM_F) libPARI$(LIB_EXT)
	$(AR) $(ADD_AR_OPT) $(MY_AR_PRE_TARGET)libPARI$(LIB_EXT) $(MY_AR_OBJECT)
	$(RANLIB) libPARI$(LIB_EXT)

' . qq{
$create_mp
pariinl.h: @$inlines_cond @$inlines
$prepend_inl	\$(PERL) -pe1 @$inlines			  >> \$@

$converted

mp\$(OBJ_EXT): $mp pariinl.h
	\$(CCCMD) \$(CCCDLFLAGS) \$(DEFINE) \$(MY_CC_PRE_TARGET)\$@ $mp

kernel\$(OBJ_EXT): $kernel{converted1} pariinl.h
	\$(ASSCMD) \$(ASSDEFINE) \$(MY_CC_PRE_TARGET)\$@ $kernel{converted1}

kernel2\$(OBJ_EXT): $kernel{converted2} pariinl.h
	\$(ASSCMD) \$(ASSDEFINE) \$(MY_CC_PRE_TARGET)\$@ $kernel{converted2}

mpinl\$(OBJ_EXT): $mpinl $mpinlh pariinl.h
	\$(CCCMD) \$(CCCDLFLAGS) \$(DEFINE) \$(MY_CC_PRE_TARGET)\$@ $mpinl

} .
    join "\n", map { <<EOF } sort keys %cfiles;
$_\$(OBJ_EXT): $cfiles{$_} pariinl.h paricfg.h
	\$(CCCMD) \$(CCCDLFLAGS) \$(DEFINE) \$(MY_CC_PRE_TARGET)\$@ $cfiles{$_}
EOF
}


# Rewrite pic option to PIC in CCCDLFLAGS,
# Why manipulate LD* when no ld is done here (static build only?)
sub MY::const_config
{
 my $self = shift;
 my $flags = $self->{'CCCDLFLAGS'}; # Tmp var needed with Perl4 !
 $flags =~ s/(-[fK]?\s*)pic\b/${1}PIC/;
 $flags =~ s/-KPIC/-K PIC/;	# Apparently needed on Solaris...
 $self->{'CCCDLFLAGS'} = $flags;
 if ($^O eq 'MSWin32' && $Config{'ccflags'} =~ /-DPERL_OBJECT/)
  {
   $self->{'LDFLAGS'} =~ s/-(debug|pdb:\w+)\s+//g;
   $self->{'LDDLFLAGS'} =~ s/-(debug|pdb:\w+)\s+//g;
  }
 return $self->MM::const_config;
}