#!/usr/bin/perl
# -*- perl -*-

eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if 0;

use strict;
use POSIX;
use FindBin qw($RealBin);

# Calling DyALog compiler

my $CC= 'gcc';

my $prefix="/usr";
my $exec_prefix="/usr";

## A better way should be found to define this variable
my $libsuffix= ("linux-gnu" =~ /^darwin/) ? ".dylib" : ".so";

#my $includedir="/usr/include";
#my $pkgincludedir="$includedir/DyALog";
#my $libdir="/usr/lib";
#my $pkglibdir="$libdir/DyALog";

my $dyalogdir="/usr/lib/DyALog";
my $includedir="$dyalogdir";
my $pkgincludedir="$dyalogdir";
my $libdir="$dyalogdir";
my $pkglibdir="$dyalogdir";

my $dyalog = "dyalog";
my $ma = "dyam2asm";

my $libtool=undef;
my $fast_install=undef;

my $analyze =0;

my @CPPFLAGS=split(/\s+/,'');
my @CFLAGS=split(/\s+/,'-Wall -O6 -mcpu=pentium');
my @LIBS = split(/\s+/,'-lbuiltins -ldyalog -lgc -lm -ldl ');

my @DFLAGS=();
my @LFLAGS=();
my @MAFLAGS=();

my @INCLUDES=();

# if an option is unknown, used for dyalog if oclass=0, for gcc otherwise
my $oclass=0; 

my @plfiles=();
my @mafiles=();
my @ofiles=();
my @sfiles=();
my @cfiles=();
my @tmpfiles=();
my $libtool_missing_relink=0;

my $outputfile;

my $dev = 0;
my $docpp = 0;
my $doma = 1;
my $dolink = 1;
my $savetmp=0;
my $verbose = 0;
my $lang= 'none';

# need proper distinction between $dir and $bindir when
# build directory is different of src directory
# for instance, when running make distcheck !
my $dir;
my $bindir;

my $dyalog_tfsfile;
my @tfsflags=();

@ARGV=(split(' ',$ENV{DYACC}),@ARGV);

# Detect if the script is run from the package or the installation place
unshift(@ARGV,'--dev') if ($RealBin =~ m{^/home/mandrake/rpm/BUILD/DyALog-1.12.0});

my @plext = ( 'pl', 'dcg', 'cfg', 'tag', 'rcg', 'tig' );
my $plext_pat = join('|',@plext);

while (@ARGV) {

    $_ = shift;

    if (/^-{1,2}help$/) {
	open(HELP,"-|") || system perldoc,"$0";
	print <HELP>;
	exit;
    }
    
    # Special option to be used only when the package is not installed
    # should no longer be explicitely used (this script detects if it installed
    # or not)
    if (/^-{1,2}dev$/) {
	$dev=1;
	$dir= "/home/mandrake/rpm/BUILD/DyALog-1.12.0";
	$bindir="/home/mandrake/rpm/BUILD/DyALog-1.12.0";
	$dyalog = "$bindir/Compiler/dyalog";
	$ma = "$bindir/Ma2Asm/dyam2asm";
	$libtool="$bindir/libtool";
	next;
    }

    if (/^-{1,2}libtool$/) {
	$libtool=shift;
	next;
    }

    if (/^-{1,2}no-fast-install$/) {
	$fast_install=0;
	next;
    }

    if (/^-{1,2}fast-install$/) {
	$fast_install=1;
	next;
    }

    # Configuring

    if (!$oclass && /^-v$/) {
	$verbose=1;
	next;
    }

    if (/^-L(.*)$/) {
	my $libpath = $1;
	$ENV{'LIBRARY_PATH'}   = "$libpath:$ENV{LIBRARY_PATH}";
	$ENV{'LD_LIBRARY_PATH'} = "$libpath:$ENV{LD_LIBRARY_PATH}";
	push(@CFLAGS,$_);
	next;
    }

    if (/^-{1,2}save-temps$/) {
	$savetmp=1;
	next;
    }

    if (/^-{1,2}pl-ext$/) {
	my $suffixe = shift;
	$suffixe =~ s/^\.//;
	push(@plext,$suffixe);
	$plext_pat = join('|',@plext);
	next;
    }

    if (/^-{1,2}cpp$/) {		# to use macro processing with cpp
	$docpp=1;
	next;
    }

    if (/^-D(.*)$/) {
	push(@CPPFLAGS,"$_");
	push(@CFLAGS,"$_");
	next;
    }

    if (/^-f(.*)$/){
      push(@CFLAGS,"$_");
      next;
    }

    # Collecting files to process

    if (/^(.+)\.($plext_pat)$/o) {
	push(@plfiles,"$_");
	next;
    }

    if (/^(.+)\.ma$/) {
	push(@mafiles,"$1");
	next;
    }

    if (/\.o$/) {
	push(@ofiles,$_);
	next;
    }

    if (/\.s$/) {
	push(@sfiles,$_);
	next;
    }

    # other files
     if (!/^-/) {
	 if ($lang eq 'pl') {
	     push(@plfiles,$_);
	 } elsif ($lang eq 'ma') {
	     push(@mafiles,$_);
	 } else {
	     push(@cfiles,$_);
	 }
	 next;
     }

    ## Processing level

    if (/^-x$/) {
	$lang = shift;
	next;
    }

    if (/^-{1,2}ma$/) {
	$doma=0;
	next;
    }

    if (/^-[cSE]$/) {
	push(@CFLAGS,$_);
	$dolink=0;
	next;
    }

    ## Output

    if (/^-o$/) {
	$outputfile = shift;
	next;
    }


    # options specific to dyalog


    if (/^-{1,2}parse$/ 
	|| /^-{1,2}rcg$/ 
	|| /^-{1,2}autoload$/ 
	|| /^-{1,2}warning$/ 
	|| /^-{1,2}verbose_tag$/) {
      s/^--/-/o;
      push(@DFLAGS,$_);
      next;
    }

    if (/^-I$/) {
	push(@INCLUDES,'-I',shift);
	next;
    }

    if (/^-{1,2}res$/) {		# dyalog will load a resource file
	push(@DFLAGS,'-res',shift);
	next;
    }

    if (/^-{1,2}guide$/) {
	push(@DFLAGS,'-guide',shift);
	next;
    }

    if (/^-{1,2}analyze$/) {
	push(@DFLAGS,'-analyze',shift);
	$analyze = 1;
	next;
    }

    if (/^-{1,2}tfs$/) {
	my $lib=shift;
	($dyalog_tfsfile,@tfsflags) = &tfsfile($lib);
	push(@DFLAGS,'-tfs',"$dyalog_tfsfile");
	next;
    }

    if (/^-pg$ || /^-g$/) {
	push(@CFLAGS,$_);
	push(@LFLAGS,$_);
	push(@MAFLAGS,$_);
	next
    }

    if (/^-{1,2}verbose$/) {
	$verbose = 1;
	next
    }

    # after -- options are by default for GCC
    last if (/^--$/);

    push(@DFLAGS,$_);

}

push(@CFLAGS,@ARGV);

if (!$dev) {
  $ENV{'LIBRARY_PATH'} = "$pkglibdir:$ENV{LIBRARY_PATH}";
  push(@CFLAGS,"-I$pkgincludedir");
} else {
  push(@CFLAGS,"-I$dir/Runtime","-I$bindir/Runtime");
  push(@INCLUDES,'-I',"$dir/Builtins");
}

if ($analyze) {
  $doma = 0;
  $dolink = 0;
}

# Adding some implicit DFLAGS

# adding -rcg if some .rcg file to compile
push(@DFLAGS,'-rcg') if (grep(/\.rcg$/,@plfiles) && !grep($_ eq '-rcg',@DFLAGS));

if ($verbose) {
    print STDERR <<MSG ;
Default options: $ENV{DYACC}
prolog files: @plfiles
ma     files: @mafiles
other  files: @cfiles

MSG
}

my $nbrfiles = @plfiles + @mafiles + @cfiles + @sfiles + @ofiles;
	
if (!$nbrfiles) {
    die "need at least one input file";
}



if (!$dolink && !$analyze && $nbrfiles > 1) {
    die "do not allow more than one input file when no link or no analyze";
}

## From .pl to .ma using dyalog

foreach my $file (@plfiles) {
    my ($basefile,$mafile);
    if ($file =~ /^(.+)\.($plext_pat)$/) {
	$basefile = $1;
    } else {
	$basefile = $file;
    }
    if ( !$doma && "$outputfile") {
      $mafile = $outputfile;
    } elsif (!$doma && !$analyze) {
      $mafile = "$basefile.ma";
      push(@mafiles,$basefile) if ($doma);
    } elsif ($analyze) {
      ## nothing to do
    } else {
	$mafile = POSIX::tmpnam();
	push(@mafiles,$mafile);
	$mafile .= ".ma";
	push(@tmpfiles,$mafile);
	if (!$outputfile && !$dolink) {
	    $outputfile = "$basefile.s"  if grep(/-S/,@CFLAGS);
	    $outputfile = "$basefile.o"  if grep(/-c/,@CFLAGS);
	}
    }
    if ($docpp) {		# preprocessing with cpp
	my ($cppfile) = POSIX::tmpnam().".S";
	push(@tmpfiles,$cppfile);
	print STDERR join(' ',"cpp $file"),"\n" if $verbose;
#	open(CPP,"/lib/cpp $file|");
	open(CPP,"-|") || exec '/lib/cpp',@CPPFLAGS,"$file";
	open(OUTCPP,">$cppfile");
	while (<CPP>) {
	    print OUTCPP unless /^\#/;
	}
	close(OUTCPP);
	close(CPP);
	$file=$cppfile;
    }
    my @output = $mafile ? ('-o',"$mafile") : () ;
    my @files = $analyze ? @plfiles : $file;
    print STDERR join(' ',$dyalog,@INCLUDES,'-a',@DFLAGS,@files,@output),"\n" if $verbose;
    system($dyalog,@INCLUDES,'-a',@DFLAGS,@files,@output) == 0 || exit(1);
    last if ($analyze);
}

goto CLEAN unless $doma;

## From .ma to .s using dyam2asm

foreach my $file (@mafiles) {
    my ($sfile);
    if (grep(/-S/,@CFLAGS)) {
	$sfile = $outputfile || "$file.s";
    } else {
	$sfile = POSIX::tmpnam().".s";
	push(@tmpfiles,$sfile);
	$libtool_missing_relink=1;
    }
    push(@sfiles,$sfile);
    print STDERR join(' ',$ma,"$file.ma",@MAFLAGS,'-o',"$sfile"),"\n" if $verbose;
    system($ma,@MAFLAGS,"$file.ma",'-o',"$sfile") == 0 || exit(1);
}

goto CLEAN if (grep(/-S/,@CFLAGS));

## From .s to .o and link using gcc

if ($dolink) {
    if (!$dev) {
	push(@CFLAGS, @tfsflags, "-L$pkglibdir");
    } else {
	push(@CFLAGS, 
	     @tfsflags,
	     # next option favor developper side in -dev mode:
	     # no need for libtool to relink uninstalled binaries before running them
	     "$bindir/Runtime/libdyalog.la",
	     "$bindir/Builtins/libbuiltins.la"
	     );
	@LIBS = ();
    }
}

$fast_install = (!$libtool_missing_relink) 
  if (defined $libtool && !defined $fast_install);

push(@CFLAGS,'-no-fast-install') if ($dolink && defined $libtool && !$fast_install);
push(@CFLAGS,'-o',"$outputfile") if "$outputfile";

## Added --mode=link to avoid warning in new versions of Libtool (> 1.5.2)
my @CC = (!$dev && !defined $libtool) ? ($CC) : ($libtool,'--mode=link',$CC);

print STDERR join(' ',@CC,@CFLAGS,@cfiles,@sfiles,@ofiles,@LIBS),"\n"
	if $verbose;
system @CC,@CFLAGS,@cfiles,@sfiles,@ofiles,@LIBS;    

CLEAN:

    if (@tmpfiles && !$savetmp) {
	print STDERR join(' ','rm',@tmpfiles),"\n" if $verbose;
	system 'rm',@tmpfiles;
	if (defined $libtool && $dolink && $libtool_missing_relink) {
	    # libtool may have to run again the link command (first run of the binary or installation)
	    # which raises problemes when tmp files have been removed !
	    # So we emit a warning
	    my $message = ($fast_install) ? 'running your binary' : 'installing your binary with libtool';
	    print STDERR <<EOF ;
----------------------------------------------------------------------
                          WARNING
dyacc has used libtool 
      and removed some tmp files needed to relink your binary
you may get an error when $message
----------------------------------------------------------------------
EOF
       }
    }

exit 0;

sub tfsfile {
    my $lib = shift;
    
    if ($lib =~ m%^(.+/)?lib(.+)\.la%) {
	# should be used only with libtool !
	my ($dir,$name) = ($1,$2);
	$dir = "./" if ($dir eq "" );
	$ENV{'LD_LIBRARY_PATH'} = "$1/.libs:$ENV{LD_LIBRARY_PATH}";
	my $dlopen = "lib$name";
	$dlopen .= "$libsuffix" unless (-e "$dir/.libs/$dlopen");
	die "Missing tfs library $lib" unless (-e "$dir/.libs/$dlopen");
	# return ("lib$name.so",$lib);
	return ($dlopen,$lib);
    }

    if ($lib =~ m%^(.+/)?lib(.+)\.so(\.\d+)*%){
	return ($lib,$lib);
    }

    return ("lib$lib.so","-l$lib");

}

__END__

=head1 NAME

dyacc - Integrated compiler for DyALog programs

=head1 SYNOPSIS

B<dyacc> [B<option> | F<filename>]...

=head1 DESCRIPTION

B<dyacc> integrates in a single Perl script the calls to B<dyalog>,
B<dyam2asm>, and B<gcc> that are needed to compile DyALog programs.

Suffixes of source file names indicate the language and kind of
processing to be done:

=over 5

=item B<.pl,.tag,.tig,.rcg> DyALog source: compile to B<.ma> using B<dyalog>

=item B<.ma> DyALog Assembler source: compile to B<.s> using B<dyam2asm>

=back

File with other suffixes are passed to B<gcc>. Common cases include
B<.c>, B<.h>, B<.s>, and B<.o>.

=head1 OPTIONS

Long options may be prefixed by '-' or by '--' (i.e. B<-verbose> or
B<--verbose>).

=over 5

=item B<-analyze F<Kind>> 

Run a DyALog analyze of sources to extract information (Kind in
lc,tag2tig)

=item B<-c> 

Compile or assemble the source files, but do not link. The compiler
output is an object file corresponding to each source file.

=item B<-dev> 

[Deprecated] To be used when B<DyALog> is not installed (development mode)

=item B<-I F<path>> 

Add F<path> to the set of pathes used by B<dyalog> to find files.

=item B<-ma> 

Compile DyALog source files, but do not call B<dyam2asm> or
B<gcc>.

By default, B<dyacc> makes the object file name for a source file by
replacing the suffixes `.pl', `.dcg', `.cfg' or `.tag' with `.ma'.
Use `-o' to select another name.

=item B<-S> 

Compile DyALog source files and call B<dyam2asm>, but not B<gcc>.

By default, B<dyacc> makes the object file name for a source file by
replacing the suffixes `.pl', `.dcg', `.cfg' or `.tag' with `.s'.
Use `-o' to select another name.

=item B<-o F<file>> 

Place output in F<file>.  This applies regardless to whatever sort of
output B<dyacc> is producing.

=item B<-parse> 

Set option -parse for B<dyalog> (parsing from connection fact 'C'(Left,Toekm,Right)

=item B<-rcg> 

Set option -rcg for B<dyalog> (for RCG grammars)

=item B<-verbose_tag> 

Set option -verbose_tag for B<dyalog>, to get more verbose forest for TAGs

=item B<-guide F<Kind>> 

Activate guiding (when possible)

=item B<-autoload> 

Set option -autoload for B<dyalog>, to activate grammar partial
autoloading

=item B<-pl-ext F<suffixe>>

Specify an extra F<suffixe> for Prolog files

=item B<-save-temps> 

Keep intermediate files (.ma and .s) but do not transmit the option to
gcc.

=item B<-v> 

Print (on standard error output) the commands executed to run the
stages of compilation.

=item B<-x F<lang>> 

Specify explicitly the F<language> for the following input files
(rather than choosing a default based on the file name suffix).  This
option applies to all following input files until the next `-x'
option. Possible values of F<language> are `pl', `ma', and `none' (to
reset).

=item B<-libtool F<pgm>>

Tell to use <pgm> as libtool program to compile and link.

=item B<-tfs F<library>>

Extend dyalog with Typed Feature Structure F<library> (see lib2tfs).

=item B<-res F<file>>

Tell B<dyalog> to load F<file> as a resource file

=item B<-warning>

Tell B<dyalog> to display some warnings.

=item B<-cpp>

Preprocess prolog files with B<cpp> (beware of strange outputs !)

=item B<-Dname[=value]>

This option is passed to cpp and gcc.

=item B<-f*>

These options are passed to gcc.

=item B<-pg>

Activate profiling

=item B<-[no-]fast-install>

Enable or disable Libtool fast-install (see libtool)

=item B<--> 

Mark the end of DyALog options. Everything on the right is passed to
B<gcc> and not considered as a B<dyacc> option.

=back

=head1 ENVIRONMENT

Any switches in the B<DYACC> environment variable will be used before
the command line arguments, but only for options allowed before the
marker `--'.

=head1 SEE ALSO

dyalog, dyam2asm, gcc, tfs2lib as man pages or as info entries.

=head1 AUTHORS

Eric de la Clergerie <Eric.De_La_Clergerie@inria.fr>

=cut

### Local Variables: 
### comment-column:0 
### comment-start: "### "  
### comment-end:"" 
### mode: perl
### End: 
