//==================================================================================================
// M e t a _ m o d e l                                                                    Interface
// M a k e f i l e _ g e n e r a t i o n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to generate the main part of a makefile from file
   dependencies. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "meta_model/makefile_generation.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guMetaModelMakefileGeneration
#define guMetaModelMakefileGeneration

// Headers //---------------------------------------------------------------------------------------
#include <bpp/meta_model/structure.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef META_MODEL_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  metaModelMakefileGeneration
#define private_area metaModelMakefileGeneration_private

namespace public_area  { /*NAMESPACE*/ using namespace metaModelStructure; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniMetaModelMakefileGeneration
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area  {
 function void makeRules(tcString,clDependency &,tyBoolean,tyBoolean=true);
}

namespace private_area {
 function void makeDynamicLibraryRules(clOutStream &,clDependency &,tyBoolean);
 function void makeObjectRules(clOutStream &,clDependency &,tyBoolean,tyBoolean);
 function void makeSignature(clOutStream &);
 function void makeStaticLibraryRules(clOutStream &,clDependency &,tyBoolean);
}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
extern_static_constant(private,tcString,goAddLibAlias,?);
extern_static_constant(private,tcString,goCompileCppAlias,?);
extern_static_constant(private,tcString,goCompileJavaAlias,?);
extern_static_constant(private,tcString,goDisplayAlias,?);
extern_static_constant(private,tcString,goDllNameAlias,?);
extern_static_constant(private,tcString,goDynamicAlias,?);
extern_static_constant(private,tcString,goLibNameAlias,?);
extern_static_constant(private,tcString,goMoveAlias,?);
extern_static_constant(private,tcString,goObjNameAlias,?);
extern_static_constant(private,tcString,goRanlibAlias,?);
extern_static_constant(private,tcString,goRemoveAlias,?);
extern_static_constant(private,tcString,goStaticAlias,?);

// X X X  Interface //------------------------------------------------------------------------------
namespace {}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// X X X  Inline //---------------------------------------------------------------------------------
namespace {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// M e t a _ m o d e l                                                               Implementation
// M a k e f i l e _ g e n e r a t i o n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "meta_model/makefile_generation.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define META_MODEL_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/meta_model/makefile_generation.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  metaModelMakefileGeneration
#define private_area metaModelMakefileGeneration_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Meta_model/Makefile_generation");

// Initialization //--------------------------------------------------------------------------------
#undef iniMetaModelMakefileGeneration
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
static_constant(tcString,goAddLibAlias);
static_constant(tcString,goCompileCppAlias);
static_constant(tcString,goCompileJavaAlias);
static_constant(tcString,goDisplayAlias);
static_constant(tcString,goDllNameAlias);
static_constant(tcString,goDynamicAlias);
static_constant(tcString,goLibNameAlias);
static_constant(tcString,goMoveAlias);
static_constant(tcString,goObjNameAlias);
static_constant(tcString,goRanlibAlias);
static_constant(tcString,goRemoveAlias);
static_constant(tcString,goStaticAlias);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //----------------------------------------------------------------------------------------MakeRules
 /*FUNCTION*/
 /* Based on file dependencies, writes in a file the makefile rules to compile and build a
    library. */
 function void makeRules(tcString agFileName,clDependency & agDependency,
                         tyBoolean agParallel,tyBoolean agDisplayed) {
  method_name("makeRules");

  clOutFile lcFile;

  open(lcFile,agFileName,ios::out|ios::trunc);
  lcFile << "# Part automatically generated by" << end_line;
  private_area::makeSignature(lcFile);

  if (agDisplayed) {
   environment::nextLine();
   environment::inform("Making Object Rules...");
  }

  private_area::makeObjectRules(lcFile,agDependency,agParallel,agDisplayed);

  if (agDisplayed) {
   environment::nextLine();
   environment::inform("Making Dynamic Library Rules...");
  }

  private_area::makeDynamicLibraryRules(lcFile,agDependency,agDisplayed);

  if (agDisplayed) {
   environment::nextLine();
   environment::inform("Making Static Library Rules...");
  }

  private_area::makeStaticLibraryRules(lcFile,agDependency,agDisplayed);
  lcFile << "# End of the part generated by" << end_line;
  private_area::makeSignature(lcFile);
  close(lcFile);
  if (lcFile.fail()) send_error(erFileWriting);
 }
}

namespace private_area {
 //--------------------------------------------------------------------------MakeDynamicLibraryRules
 function void makeDynamicLibraryRules(clOutStream & agStream,clDependency & agDependency,
                                       tyBoolean agDisplayed) {
  clLibraryDependencyX::const_iterator lcCurrentLibrary = agDependency.libraries().begin();
  clLibraryDependencyX::const_iterator lcLastLibrary    = agDependency.libraries().end();

  tyCardinal              lcCount;
  tyCardinal              lcCounter;
  clFileS::const_iterator lcCurrentFile;
  clFileDependency *      lcFile;
  clFileS::const_iterator lcLastFile;
  ctLibraryDependency *   lcLibrary;
  clString                lcShortName;
  clString                lcUpperCase;

  while (lcCurrentLibrary!=lcLastLibrary) {
   lcLibrary=&((*lcCurrentLibrary).second);

   if (lcLibrary->files().size()>0 and lcLibrary->name()!=""
       and lcLibrary->alias()!="bpp_display"
       and (not matched(lcLibrary->alias().data(),"jirk_*"))) {
    if (agDisplayed) environment::out(lcLibrary->alias().data(),true,true);

    lcCounter=lcLibrary->dllName().size()-1;
    while (lcCounter>0 and lcLibrary->dllName()[lcCounter]!='/') lcCounter--;
    lcShortName=lcLibrary->dllName().part(lcCounter+1,lcLibrary->dllName().size()-lcCounter);
    lcUpperCase=lcLibrary->alias();
    upperCase(lcUpperCase.data());

    agStream << lcLibrary->dllAlias() << " :";
    lcCount=0;
    lcCurrentFile=lcLibrary->files().begin();
    lcLastFile=lcLibrary->files().end();

    while (lcCurrentFile!=lcLastFile) {
     lcFile=&(agDependency.files()[*lcCurrentFile]);

     if (lcFile->isJava())  agStream << " \\" << end_line << ' ' << lcFile->objectName() << '_';
     else ++lcCount;

     ++lcCurrentFile;
    }

    if (lcCount>0) agStream << " \\" << end_line << ' ' << lcLibrary->dllName();
    agStream << end_line << end_line;

    if (lcCount>0) {
     agStream << lcLibrary->dllName() << " :";
     agStream << " \\" << end_line << " $(" << lcUpperCase << "_DLL_NEED)";
     lcCurrentFile=lcLibrary->files().begin();

     while (lcCurrentFile!=lcLastFile) {
      lcFile=&(agDependency.files()[*lcCurrentFile]);
      if (not lcFile->isJava()) agStream << " \\" << end_line << ' ' << lcFile->objectName();
      ++lcCurrentFile;
     }

     agStream << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goDisplayAlias << ") ";
     agStream << lcShortName << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goRemoveAlias << ") ";
     agStream << lcLibrary->dllName() << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goDynamicAlias << ")";
     agStream << " $(" << private_area::goDllNameAlias << ")" << lcLibrary->dllName();
     agStream << " $(" << lcUpperCase << "_DEPENDENCY)";
     lcCurrentFile=lcLibrary->files().begin();

     while (lcCurrentFile!=lcLastFile) {
      lcFile=&(agDependency.files()[*lcCurrentFile]);

      if (not lcFile->isJava()) {
       agStream << " \\" << end_line;
       agStream << horizontalTab() << " " << lcFile->objectName();
      }

      lcCurrentFile++;
     }

     agStream << end_line << end_line;
    }
   }

   lcCurrentLibrary++;
  }
 }
 //----------------------------------------------------------------------------------MakeObjectRules
 function void makeObjectRules(clOutStream & agStream,clDependency & agDependency,
                               tyBoolean agParallel,tyBoolean agDisplayed) {
  clFileDependencyX::const_iterator lcCurrentFile = agDependency.files().begin();
  clFileDependencyX::const_iterator lcLastFile    = agDependency.files().end();

  tyCardinal              lcCounter;
  clFileS::const_iterator lcCurrentFile2;
  ctFileDependency *      lcFile;
  clFileS::const_iterator lcLastFile2;

  while (lcCurrentFile!=lcLastFile) {
   lcFile=&(*lcCurrentFile).second;

   if (lcFile->objectName()!="" and (not matched(lcFile->alias().data(),"jirk/*"))) {
    if (agDisplayed) environment::out(lcFile->alias().data(),true,true);

    if (lcFile->isJava()) {
     agStream << lcFile->objectName() << "_ :" << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goDisplayAlias << ") ";
     agStream << lcFile->alias() << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goCompileJavaAlias << ") ";
     agStream << lcFile->sourceName() << end_line << end_line;
    }
    else {
     agStream << lcFile->objectName() << " :";
     lcCurrentFile2=lcFile->files().begin();
     lcLastFile2=lcFile->files().end();

     while (lcCurrentFile2!=lcLastFile2) {
      if (agDependency.files().count(*lcCurrentFile2)==1)
       agStream << " \\" << end_line << ' ' << agDependency.files()[*lcCurrentFile2].sourceName();

      lcCurrentFile2++;
     }

     agStream << " \\" << end_line << ' ' << lcFile->sourceName() << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goDisplayAlias << ") ";
     agStream << lcFile->alias() << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goCompileCppAlias << ") ";

     if (agParallel) {
      agStream << lcFile->sourceName() << " \\" << end_line;
      agStream << horizontalTab() << "$(" << private_area::goObjNameAlias << ") ";
      agStream << lcFile->objectName() << end_line << end_line;
     }
     else {
      agStream << lcFile->sourceName() << end_line;
      agStream << horizontalTab() << "@$(" << private_area::goMoveAlias << ") ";
      lcCounter=lcFile->objectName().size()-1;
      while (lcCounter>0 and lcFile->objectName()[lcCounter]!='/') lcCounter--;
      agStream << lcFile->objectName().part(lcCounter+1,lcFile->objectName().size()-lcCounter);
      agStream << ' ' << lcFile->objectName() << end_line << end_line;
     }
    }
   }

   lcCurrentFile++;
  }
 }
 //------------------------------------------------------------------------------------MakeSignature
 function void makeSignature(clOutStream & agStream) {
  agStream << "# " << environment::programName() << end_line;
  agStream << "# " << environment::copyright() << end_line;
  agStream << "# " << environment::programmerName() << end_line;
  agStream << "# " << environment::programmerEmail() << end_line;
  agStream << "# " << environment::programmerURL() << end_line;
  agStream << end_line;
 }
 //---------------------------------------------------------------------------MakeStaticLibraryRules
 function void makeStaticLibraryRules(clOutStream & agStream,clDependency & agDependency,
                                      tyBoolean agDisplayed) {
  clLibraryDependencyX::const_iterator lcCurrentLibrary = agDependency.libraries().begin();
  clLibraryDependencyX::const_iterator lcLastLibrary    = agDependency.libraries().end();

  tyCardinal              lcCount;
  tyCardinal              lcCounter;
  clFileS::const_iterator lcCurrentFile;
  clFileDependency *      lcFile;
  clFileS::const_iterator lcLastFile;
  ctLibraryDependency *   lcLibrary;
  clString                lcUpperCase;

  while (lcCurrentLibrary!=lcLastLibrary) {
   lcLibrary=&((*lcCurrentLibrary).second);

   if (lcLibrary->files().size()>0 and lcLibrary->name()!=""
       and not matched(lcLibrary->alias().data(),"bpp_program*")
       and lcLibrary->alias()!="bpp_display"
       and (not matched(lcLibrary->alias().data(),"jirk_*"))) {
    if (agDisplayed) environment::out(lcLibrary->alias().data(),true,true);
    lcCurrentFile=lcLibrary->files().begin();
    lcLastFile=lcLibrary->files().end();
    lcCount=0;
    agStream << lcLibrary->alias() << " :";

    while (lcCurrentFile!=lcLastFile) {
     lcFile=&(agDependency.files()[*lcCurrentFile]);

     if (lcFile->isJava()) agStream << " \\" << end_line << ' ' << lcFile->objectName() << '_';
     else ++lcCount;

     ++lcCurrentFile;
    }

    if (lcCount>0) agStream << " \\" << end_line << ' ' << lcLibrary->name();
    agStream << end_line << end_line;

    if (lcCount>0) {
     agStream << lcLibrary->name() << " :";
     lcUpperCase=lcLibrary->alias();
     upperCase(lcUpperCase.data());
     agStream << " \\" << end_line << " $(" << lcUpperCase << "_LIB_NEED)";
     lcCurrentFile=lcLibrary->files().begin();

     while (lcCurrentFile!=lcLastFile) {
      lcFile=&(agDependency.files()[*lcCurrentFile]);
      if (not lcFile->isJava()) agStream << " \\" << end_line << ' ' << lcFile->objectName();
      ++lcCurrentFile;
     }

     agStream << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goDisplayAlias << ") ";
     lcCounter=lcLibrary->name().size()-1;
     while (lcCounter>0 and lcLibrary->name()[lcCounter]!='/') lcCounter--;
     agStream << lcLibrary->name().part(lcCounter+1,lcLibrary->name().size()-lcCounter);
     agStream << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goRemoveAlias << ") ";
     agStream << lcLibrary->name() << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goStaticAlias << ")";
     agStream << " $(" << private_area::goLibNameAlias << ")" << lcLibrary->name();
     lcCurrentFile=lcLibrary->files().begin();

     while (lcCurrentFile!=lcLastFile) {
      lcFile=&(agDependency.files()[*lcCurrentFile]);

      if (not lcFile->isJava()) {
       agStream << " \\" << end_line;
       agStream << horizontalTab() << " $(" << private_area::goAddLibAlias << ")";
       agStream << lcFile->objectName();
      }

      lcCurrentFile++;
     }

     /*
     lcCurrentFile=lcLibrary->files().begin();

     while (lcCurrentFile!=lcLastFile) {
      lcFile=&(agDependency.files()[*lcCurrentFile]);

      if (not lcFile->isJava()) {
       agStream << horizontalTab() << "@$(" << private_area::goStaticAlias << ")";
       agStream << " $(" << private_area::goLibNameAlias << ")" << lcLibrary->name();
       agStream << " $(" << private_area::goAddLibAlias << ")" << lcFile->objectName();
       agStream << end_line;
      }

      ++lcCurrentFile;
     }
     */

     agStream << end_line;
     agStream << horizontalTab() << "@$(" << private_area::goRanlibAlias << ") ";
     agStream << lcLibrary->name() << end_line << end_line;
    }
   }

   lcCurrentLibrary++;
  }
 }
}

// X X X  Implementation //-------------------------------------------------------------------------
namespace {}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    goAddLibAlias      = "ADD_LIB";
    goCompileCppAlias  = "COMPILE_OBJ";
    goCompileJavaAlias = "COMPILE_CLASS";
    goDisplayAlias     = "ECHO";
    goDllNameAlias     = "DLL_NAME";
    goDynamicAlias     = "DYNAMIC";
    goLibNameAlias     = "LIB_NAME";
    goMoveAlias        = "MOVE";
    goObjNameAlias     = "OBJ_NAME";
    goRanlibAlias      = "RANLIB";
    goRemoveAlias      = "REMOVE";
    goStaticAlias      = "STATIC";
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) { environment::informTermination(goModuleName); }
}

// End //-------------------------------------------------------------------------------------------
}