#! perl -w

=head1 NAME

cfx5mkext - Build ANSYS CFX Shared Library Extensions

=cut

#!/bin/sh
#==============================================================================
# @(#) $Id$
#------------------------------------------------------------------------------
##
## Name:
##
## cfx5mkext
##
## Purpose:
##
## Build ANSYS CFX Shared Library Extensions
##
## Synopsis:
##
## cfx5mkext [-name <name>] [-dest <directory>] [-<option>...]
##           [-WC,<option>...]  [-WF,<option>...]  [-WL,<option>...]
##           [file...]
##
## []  denotes an optional argument.
## |   separates mutually exclusive arguments.
## ... following an argument indicates that it may be repeated.
## <>  and # denote that substitution of a suitable value is required. All 
##     other arguments are keywords, some of which may have a short form.
##
## Arguments:
##
## The following arguments are recognised by the cfx5mkext command:
##
#A -c
##     Compiles the supplied source, but does not link.
##
#A -dest <dir>
##     Sets the directory in which to place the built library.  The
##     actual library file will be written to a subdirectory of this,
##     depending on the current architecture and options.
##
#A -double
##     Build the library for use with the ANSYS CFX double precision
##     Solver.
##
#A -large
##     Build the library for use with the ANSYS CFX large problem
##     (64 bit integer) Solver.
##
## -help
## -h
##     Print this message.
##
#A -name <name>
##     Sets the base name of the library.  This is used along with the
##     standard naming conventions for the current platform to name
##     the file, for example a name "my_func" might become
##     "libmy_func.so" or "my_func.dll" on different platforms.  This
##     is the same as the Library Name parameter in the CFX Command
##     Language definition.
##
## -verbose
## -v
##     Specifying this option may result in additional output being sent to
##     the UNIX standard output file (normally the screen).
##
#A -WF,...
#A -WC,...
#A -WL,...
##     These options allow you to pass new options to the Fortran
##     (-WF) or C (-WC) compilers, or the linker (-WL), explicitly.
##     This can be useful if the option you wish to pass conflicts
##     with one of the options which are used by the cfx5mkext
##     command, or for options which take space-separated arguments,
##     which will otherwise be treated as filenames.  For example, to
##     pass "-tune ev6" to the Fortran compiler, use
##         -WF,-tune,ev6
##
## -<option>
## +<option>
##     Any unrecognised options will be passed onto the compilers
##     invoked by cfx5mkext.  Normally options are passed to both C
##     and Fortran compilers, if used, but options beginning with the
##     following are passed instead to the linker:
##       -l   -L
##
## <file>
##     Any arguments which do not start with a - (or +) sign are taken
##     to be filenames.  Files ending with .c are assumed to be C
##     language; those ending with .f, .F, .f77, .f90, .for or .ftn
##     are Fortran.  Anything else will be passed straight on to the
##     linker.
##
#H Examples:
##
## To compile a single Fortran file to make a simple extension
## "my_func" in the current directory:
##
##   cfx5mkext my_func.F
##
## To build an extension from two Fortran files providing the solver
## code, a C file which interfaces to an external library, and the
## library itself, and to optimise the code which we compile (split
## over two lines for clarity, this should be only one line):
##
##   cfx5mkext -name spray -O2 spray1.F spray2.F sprayinterface.c \
##      -L/home/andy/lib -lspray
##
#U ****************************************************************************
#U *                                                                          *
#U *     The following features are supported only within CFDS and are not    *
#U *     shown by the -help option when the command is run elsewhere.         *
#U *                                                                          *
#U ****************************************************************************
#U
#U -attrs <list>
#U    Sets the attribute list to use.  Must be supported by cfx5mkext.ccl.
#U
#------------------------------------------------------------------------------

# get the standard ANSYS CFX library directory
BEGIN {
  if ($0 =~ m!^([a-z]:)?[\\/]!i) {
    # it's an absolute path, use it as-is
    (my $nd=$0) =~ s!(perllib[\\/])?[^\\/]*$!!i;
    unshift @INC, "$nd/perllib";
  } else {
    # wrap this in an eval, and if it fails, pray ... for example,
    # this can fail on Windows if we're running off a share.
    eval {
      require FindBin;
      FindBin->import();
      unshift @INC, "$FindBin::Bin/perllib";
      my $dummy=$FindBin::Bin; # kill used only once warning
    };
    warn $@ if $@;
  }
  if (defined $ENV{CFX_LOCAL_PERLLIB}) {
     unshift @INC, $ENV{CFX_LOCAL_PERLLIB};
  }

}

use CFX5::Init;
CFX5::Init::init();

use vars qw($CVSVersion);
sub BEGIN {
  $CVSVersion='@(#) $Id$\n';
}

use strict;

#BEGIN { $Exporter::Verbose=1; }

use CFX5::Output qw(:all);
use CFX5::Config qw(%Config append_to_execpath append_to_libpath prepend_to_libpath export_conf);
use CFX5::PrintConfig;
use CFX5::Help;

use CFX5::CmdLine qw(:util parse_cmdline is_std_arg is_in_arg_list);

use CFX5::ShlibExt;

#
# read the configuration
#
CFX5::Config::read_config();

#
# set up our initial settings
#
use vars qw($Name $Dest @compflags @cflags @fflags @ldflags @ldlibs @files $Attrs $CompileOnly);
$Dest='.';

$Attrs=CFX5::Attr->new($Config{CFX5_ARCH_ATTRS});

#
# we do our own command-line parsing, it's too complicated to try not to.
#
{
  my $ProcessStd=1;
  my %MyArgs=(
              '-name+'  => \$Name,
              '-dest+'  => \$Dest,
              '-attrs+' => sub { $Attrs->merge_attr_list($_[1]); },
              '-double' => sub { $Attrs->set_attr('double'); },
              '-large'  => sub { $Attrs->set_attr('int64'); },
              '-c' => \$CompileOnly,
             );

  while (@ARGV) {
    # first check if this is one of the standard ones, or one of those
    # that we accept in the normal way
    if (is_in_arg_list($ARGV[0], %MyArgs)
        or ($ProcessStd and is_std_arg($ARGV[0]))) {
      @ARGV = parse_cmdline(\@ARGV,
                            'INCLUDE_STD', 'PROCESS_SINGLE_ARG',
                            %MyArgs
                           );
      next;
    }

    #
    # bare filenames
    #
    my $arg=shift;
    unless ($arg =~ m!^[-+]!) {
      # expand wildcards.  We need this if running under the NT
      # command prompt.
      if ($arg =~ m!\*!) {
        push @files, glob($arg);
      } else {
        push @files, $arg;
      }
      next;
    }

    if ($arg =~ s!^-W([CFL]),!!) {
      # we use the uppercase letters to avoid conflicts (I hope ...)
      my $for=$1;
      my @args=split(/,/,$arg);
      if ($for eq 'C') {
        push @cflags, @args;
      } elsif ($for eq 'F') {
        push @fflags, @args;
      } elsif ($for eq 'L') {
        push @ldflags, @args;
      } else {
        internal_error("Unknown but matched Wx switch $for");
      }
      next;
    }

    #
    # we recognise some switches as linker switches
    #
    if ($arg =~ m!^-[Ll]!) {
      push @ldlibs, $arg;
      next;
    }

    #
    # the rest we assume are destined for both the compiler(s)
    #
    push @cflags, $arg;
    push @fflags, $arg;
    next;
  }
}

if( ! scalar @files ){
  _error("No source file has been specified");
}

exit_if_error();

CFX5::PrintConfig::show_vars_by_verbosity();

my $obj=CFX5::ShlibExt->new();

$obj->attrs($Attrs);

$obj->cflags_plus(@cflags, @compflags);
$obj->fflags_plus(@fflags, @compflags);
$obj->ldflags_plus(@ldflags);
$obj->ldlibs_plus(@ldlibs);

my $rv;
my $file=$obj->PrepareDirs($Name,$Dest,@files);
if ($file) {
  if ($CompileOnly) {
    $rv=$obj->CompileSource($file,@files);
  } else {
    $rv=$obj->BuildLibrary($file,@files);
  }
} else {
  $rv = '';
}

unless ($rv) {
  exit(1);
}
exit(0);
