// Gestionnaire de compilation-linker C++ - R. Ansari 10/2000
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA

#include "sopnamsp.h"
#include "cxxcmplnk.h"
#include "sspvflags.h"
#include <stdlib.h>
#include <iostream>

/*!
  \class SOPHYA::CxxCompilerLinker
  \ingroup SysTools
  This classes handles the compilation of a C++ source code and 
  building of a shared library.
  The present version has been adapted for different compilers and 
  systems:
  - Linux + GNU compiler (g++)
  - Linux + Intel compiler (icc)
  - Darwin + GNU compiler (MaxOS X 10.3 and 10.4)
  - OSF1 + cxx  (HP/Compaq/Digital OSF-Tru64 and cxx c++ compiler)
  - IRIX64 + CC : Silicon Graphics  system and C++ compiler
  - AIX + xlC : IBM  system and C++ compiler

  \sa SOPHYA::PDynLinkMgr

  \code
  #include "cxxcmplnk.h"
  CxxCompilerLinker cxx;
  string name = "toto.cc";
  string oname = "toto.o";
  string soname = "toto.so";
  int rc;
  // compiling file 
  rc = cxx.Compile(name, oname);
  // linking and building the shared object 
  rc = cxx.BuildSO(oname, soname);
  \endcode
*/

static const char * gcxx_opt = "-O -fdollars-in-identifiers";
static const char * KCC_opt = 
  "-O --exceptions --rtti  --auto_instantiation --one_instantiation_per_object -D__KCC__";
static const char * icc_opt = "-O -fpic -frtti";
static const char * cxx_opt = 
  "-O -no_implicit_include -pthread";
static const char * SGICC_opt =  "-O -prelink -D__SGICC__ -LANG:std";
static const char * xlC_opt = "-O -qrtti=all -qeh=v6";

/* --Methode-- */
/*!
  Constructor
  \param fglibsophya : if \c true libsophya.so is used when linking
  \param fglibextsophya : if \c true libextsophya.so is used when linking
  \param fglibpi : if \c true libPI.so is used when linking
*/
CxxCompilerLinker::CxxCompilerLinker(bool fglibsophya, bool fglibextsophya, bool fglibpi)
  : verbose(false)
{
  
  string syscomp = "";

#if defined(OSF1) 
  cppFlags += "-DOSF1" ; 
  syscomp = "OSF1-";
#elif defined(Linux)
  cppFlags += "-DLinux" ;
  syscomp = "Linux-"; 
#elif defined(SunOS)
  cppFlags += "-DSunOS" ;
  syscomp = "SunOS-";  
#elif defined(IRIX64)
  cppFlags += "-DIRIX64" ;
  syscomp = "IRIX64-";  
#elif defined(AIX)
  cppFlags += "-DAIX" ;
  syscomp = "AIX-";  
#elif defined(HPUX)
  cppFlags += "-DHPUX" ;
  syscomp = "HPUX-";  
#elif defined(Darwin)
  cppFlags += "-DDarwin";
  syscomp = "Darwin-";
#endif


#if defined( __GNUG__ )
  compCmd = "g++ ";
  compOptions = gcxx_opt;
  linkOptions = "-O -shared";
  syscomp += "g++/"; 
#ifndef SO_NOFPIC
  compOptions += " -fPIC ";
  linkOptions += " -fPIC ";
#endif
#endif
#if defined( __DECCXX )
  compCmd = "cxx ";
  compOptions = cxx_opt;
  linkOptions = compOptions + " -shared";
  syscomp += "cxx/"; 
#endif
#if defined( __KCC__ )
  compCmd = "KCC ";
  compOptions = KCC_opt;
  linkOptions = KCC_opt;
  syscomp += "KCC/";  
#endif
#if defined( __SGICC__ )
  compCmd = "CC ";
  compOptions = SGICC_opt;
  linkOptions = "-shared -O -LANG:std";
  syscomp += "CC/";  
#ifdef SO_ARCH64
  compOptions += " -64 ";
  linkOptions += " -64 "; 
#endif
#endif
#if defined( __INTEL_COMPILER )
  compCmd = "icpc ";
  compOptions = icc_opt;
  linkOptions = icc_opt + string(" -shared");
  syscomp += "icc/";  
#endif
#if defined( __IBMCPP__ )
  compCmd = "xlC ";
  compOptions = xlC_opt;
  linkOptions = xlC_opt + string(" -brtl -qmkshrobj ");
  syscomp += "xlC/";  
#ifdef SO_ARCH64
  compOptions += " -q64 ";
  linkOptions += " -q64 "; 
#endif
#endif

#ifndef Darwin
  linkCmd = compCmd;
#else
  linkCmd = "c++ -bundle ";
  linkOptions = "-lSystem -lm";
#endif

  cppFlags += " -I. ";
  compOptions += " -c ";

  bool fgenv1 = false;
  bool fgenv2 = false;
  char* varenv=NULL;
  varenv=getenv("SOPHYABASE");
  string sbaserep = "./";
  if (varenv) {
    fgenv1 = true;
    sbaserep = varenv; 
    if (sbaserep[sbaserep.length()-1] != '/')  sbaserep += '/';
    cppFlags += ( " -I" + sbaserep + "include/ ");
    linkOptions += " -L" + sbaserep + "slb/";
  }
  varenv=getenv("SOPHYABASEREP");
  if (varenv) {
    fgenv2 = true;
    string dpcbase = varenv; 
    if (dpcbase[dpcbase.length()-1] != '/')  dpcbase += '/';
    cppFlags += ( " -I" + dpcbase + "Include/ ");
    linkOptions += " -L" + dpcbase + syscomp + "ShLibs/";
    char * varenvextl=getenv("EXTLIBDIR");
    if (varenvextl) {
    string extlib = varenvextl; 
    if (extlib[extlib.length()-1] != '/')  extlib += '/';
    cppFlags += ( " -I" + extlib +"Include/ ");
    }
  }
  if (!fgenv1 && !fgenv2) 
    cout << " CxxCompilerLinker()/Warning : SOPHYABASE not defined" << endl;
  if (fgenv1 && fgenv2) 
    cout << " CxxCompilerLinker()/Warning : both SOPHYABASE and SOPHYABASEREP defined" << endl;

#if defined( SO_SLBALLINONE )
  if (fglibsophya || fglibpi) linkOptions += " -lAsophyaextPI ";
  /*
  cout << " +++ DBG/CxxCompilerLinker() - in AIX fglibpi= " 
       << ((fglibpi) ? " true " : " false ")  << endl;
  // Reza:Dec 2005 : pb avec les programmes PI sur AIX si linke avec shared lib PI
  // (Portage sur regatta.calcul.u-psud.fr AIX 5.3 , libXm Xt X11 en .a uniquement) 
  if (fglibpi) { 
    linkOptions += " -L" + sbaserep + "lib/";
    linkOptions += " -lPIext -lPIGcont -lPI -lXm -lXt -lX11 -lpthread ";
  }
  */
  cout << "CxxCompilerLinker()/Warning: SINGLESLB_SOPHYA_PI_EXT -> linkOptions= " << linkOptions << endl; 
#else
  if (fglibsophya) linkOptions += " -lsophya ";
  if (fglibextsophya) linkOptions += " -lextsophya ";
  if (fglibpi) linkOptions += " -lPI ";
#endif
  linkOptions += " -lm ";


  if ( (varenv=getenv("TMPDIR")) != NULL )  { 
    tmpDir = varenv; 
    if (tmpDir[tmpDir.length()-1] != '/') tmpDir += '/';
  }
}

/* --Methode-- */
CxxCompilerLinker::~CxxCompilerLinker()
{
}

/* --Methode-- */
/*!
  Compiles the file \c name using the C++ compiler driver and produces 
  the output object file \c oname. If no output name is specified,
  a default output file name is made from the input name, with the 
  suffix .o , the in temporary directory.
  \param name : input C++ source file
  \param oname : output object file 
*/
int CxxCompilerLinker::Compile(string const & name, string & oname)
{
  if (oname.length() < 1) {
    size_t l,p,q;
    l = name.length();
    p = name.rfind('/');
    if (p >= l)  p = 0;
    else p++;
    q = name.rfind('.');
    if ((q < l) && (q > p))  oname = tmpDir + name.substr(p, q-p) + ".o";
    else oname = tmpDir + name.substr(p) + ".o";
  }
  string cmd;
  cmd = compCmd + cppFlags + compOptions + "-o " + oname + " " + name ; 
  if (verbose)
    cout << "CxxCompilerLinker::Compile() - Executing \n" << cmd << endl; 
  int rc = system(cmd.c_str());
  if (rc != 0)   
    cerr << "CxxCompilerLinker::Compile() Error Rc(" << cmd <<")= "<< rc << endl; 
  
  return(rc);
}

/* --Methode-- */
/*!
  Creates a shared library from the object file \c oname.
  If no output name \c soname is specified,
  a default output file name is made from the object name, with the 
  suffix .so , in the temporary directory.
  \param oname : input object file 
  \param soname : shared library name
*/
int CxxCompilerLinker::BuildSO(string const & oname, string & soname)
{
  // const char * soext = ".dylib"; if defined(Darwin) - pas necessaire Reza 02/2002
  const char * soext = ".so";

  if (soname.length() < 1) {
    size_t l,p,q;
    l = oname.length();
    p = oname.rfind('/');
    if (p >= l)  p = 0;
    else p++;
    q = oname.rfind('.');
    if ((q < l) && (q > p))  soname = tmpDir + oname.substr(p, q-p) + soext;
    else soname = tmpDir + oname.substr(p) + soext;
  }
  string cmd;
  cmd = linkCmd + " " + oname + " " + linkOptions + " -o " + soname + " " ; 
  if (verbose)
    cout << "CxxCompilerLinker::BuildSO() - Executing \n" << cmd << endl; 
  int rc = system(cmd.c_str());
  if (rc != 0)   
    cerr << "CxxCompilerLinker::BuildSO() Error Rc(" << cmd <<")= "<< rc << endl; 
  
  return(rc);
}

