#============================================================================
# Utility rules for static builds without plugins
# Copyright (C)2003 by Matze Braun <matzebraun@users.sourceforge.net>
#
#    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.
#
#    You should have received a copy of the GNU Library General Public License
#    along with this library; if not, write to the Free Software Foundation,
#    Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#============================================================================
# all private

rule GenerateStaticPluginBoilerplate
{
  ResponseFile $(<) :
    "// This file is automatically generated."
    "#include \"cssysdef.h\""
    "#include \"csutil/scf.h\""
    ""
    "// Put static linking stuff into own section."
    "// The idea is that this allows the section to be swapped out but not"
    "// swapped in again b/c something else in it was needed."
    "#if !defined(CS_DEBUG) && defined(CS_COMPILER_MSVC)"
    "#pragma const_seg(\".CSmetai\")"
    "#pragma comment(linker, \"/section:.CSmetai,r\")"
    "#pragma code_seg(\".CSmeta\")"
    "#pragma comment(linker, \"/section:.CSmeta,er\")"
    "#pragma comment(linker, \"/merge:.CSmetai=.CSmeta\")"
    "#endif"
    : notfile ;
}

rule GenerateStaticPluginInstantiation
{
  GenerateStaticPluginBoilerplate $(<) ;
  ResponseFile $(<) : "SCF_USE_STATIC_PLUGIN($(>:B))" : notfile ;
}

rule GenerateStaticPluginRegistration
{
  GenerateStaticPluginBoilerplate $(<) ;
  GenerateStaticPluginRegistration1 $(<) : $(>) ;
}

rule BuildStaticRegFile
{
  local statictarget = $(>) ;
  # prepare source files
  local sources = [ DoObjectGrist _$(<)_static_reg.cpp ] ;
  local object = [ CompileObject $(sources) ] ;

  MakeLocate $(sources) : $(LOCATE_TARGET) ;
  MakeLocate $(object) : $(LOCATE_TARGET) ;

  local metalist metafile i ;
  for i in $(<)
  {
    metafile = $($(i)_METAFILE) ;
    metalist += $(metafile) ;
    Depends $(sources) : $(metafile) ;
  }
  
  GenerateStaticPluginRegistration $(sources) : $(metalist) ;
  Clean $(statictarget)clean : $(sources) ; 
  Clean $(statictarget)clean : $(object) ; 
  return $(object) ;
}

rule BuildStaticUseFile
{
  local statictarget = $(2) ;
  local package = $(3) ;
  # prepare source files
  local sources ;
  if $(3)
  {
    sources = [ DoObjectGrist _cs_static_use_$(package).cpp ] ;
  }
  else
  {
    sources = [ DoObjectGrist _cs_static_use.cpp ] ;
  }
  local object = [ CompileObject $(sources) ] ;

  MakeLocate $(sources) : $(LOCATE_TARGET) ;
  SEARCH on $(sources) = $(LOCATE_TARGET) ;
  MakeLocate $(object) : $(LOCATE_TARGET) ;
  
  C++FLAGS on $(object) += $($($(>).EXTERNALLIBS).CFLAGS) ;

  GenerateStaticPluginInstantiation $(sources) : $(<) ;
  Clean $(statictarget)clean : $(sources) ; 
  Clean $(statictarget)clean : $(object) ; 
  return $(object) ;
}

##  LinkStaticPlugins target : plugins [: optionalplugins] [: package]
##    Builds the plugins in 'plugins' (and 'optionalplugins') statically into 
##    'target'. Can be used both for static plugins from the local project or
##    an external project. In the latter case, the project's static plugin
##    library has to have been built, also, the "<package>.STATICDEPS" 
##    variable must contain a path to a specially generated Jamfile that 
##    contains information about the plugins provided by as well as external
##    dependencies. The static plugins library for both local and external 
##    package must be explicitly linked into 'target'.
rule LinkStaticPlugins
{
  local package target plugins optplugins ;
  # Fetch the parameters
  target = $(1) ;
  plugins = $(2) ;
  optplugins = $(3) ;
  package = $(4) ;
  
  if $(package)
  {
    # External static plugins.
    # First include static plugin info
    if ! $(HAVE_STATICDEPS.$(package))
    {
      include $($(package).STATICDEPS) ;
      HAVE_STATICDEPS.$(package) = yes ;
    }
    local lflags p ;
    # Collect optional plugins
    for p in $(optplugins)
    {
      if [ IsElem $(p) : $(STATICPLUGINS.AVAILABLE) ]
      {
	plugins += $(p) ;
      }
    }
    # Grab flags
    for p in $(plugins)
    {
      NotFile $(p) ;
      CFlags $(target) : $(STATICPLUGIN.$(p).CFLAGS) ;
      lflags = [ MergeLFlags $(lflags) : $(STATICPLUGIN.$(p).LFLAGS) ] ;
    }
    LFlags $(target) : $(lflags) ;
  }
  else
  {
    # Local static plugins
    local lflags libs p ;
    # Collect optional plugins
    for p in $(optplugins)
    {
      if $($(p)_TARGET)
      {
	plugins += $(p) ;
      }
    }
    # Grab flags
    for p in $(plugins)
    {
      lflags += [ Filter $($(p).LFLAGS) : $(LINKLIBS) $(PLUGIN.LFLAGS) ] ;
      libs += $($(p).NEEDLIBS) ;
    }
    LFlags $(target) : [ Reverse [ RemoveDups [ Reverse $(lflags) ] ] ] ;
    LinkWith $(target) : [ Reverse [ RemoveDups [ Reverse $(libs) ] ] ] ;
  }
  
  # construct initialization sourcefile
  local staticobject = [ BuildStaticUseFile $(plugins) : $(target) : $(package) ] ;
  ExtraObjects $(target) : $(staticobject) : inheritcflags ;
}

##  StaticPluginLibrary name [: rejects]
##    Create a library that monolithically contains statically linkable 
##    versions of all plugins, and also provides a target to install this
##    library ("install_staticplugins") as well as resources to facilitate the
##    use of that library from external projects. Also, most plugins will
##    probably themselves depend on other libraries; while those are correctly
##    linked in with the static plugins, they are not built or installed when
##    the static plugin library is.
##    This library is intended to be used together with the 
##    "LinkStaticPlugins" rule.
rule StaticPluginLibrary
{
  local name = $(<) ;
  local rejects = $(>) ;
  local t ;

  local targets = $(STATICPLUGINS.SUBTARGETS) ;
  if $(rejects)
  {
    targets = [ Filter $(targets) : $(rejects) ] ;
  }
  local objects ;
  for t in $(targets)
  {
    objects += $(STATICPLUGINS.OBJECTS.$(t)) ;
  }
  
  # Set up library
  Library $(name) : $(objects) : noinstall notest independent nohelp ;
  SEARCH on $(name) += $(LOCATE_TARGET) ;
  MsvcDefine $(name) : $(STATICPLUGINS.MSVC_DEFINES) ;
  CFlags $(name) : 
    [ FDefines CS_STATIC_LINKED ] 
    : nostatic ;
  
  # Copy a variety of flags
  local o ;
  for o in $(objects)
  {
    local sourceobj = $(STATICPLUGINS.OBJECTS.$(o)) ;
    if $(sourceobj)
    {
      CCFLAGS on $(o) += [ on $(sourceobj) GetVar CCFLAGS ] ;
      C++FLAGS on $(o) += [ on $(sourceobj) GetVar C++FLAGS ] ;
    }
  }

  # Write out needed CFLAGS, LFLAGS
  STATICPLUGINS.DEPENDENCIES = $(name).jam ;
  MakeLocate $(STATICPLUGINS.DEPENDENCIES) : $(LOCATE.OBJECTS)/libs ;
  for t in $(targets)
  {
    WriteDependencies $(STATICPLUGINS.DEPENDENCIES) : $(t) : $(name) ;
  }
  Depends $(name) : $(STATICPLUGINS.DEPENDENCIES) ;
  Clean $(name)clean : $(STATICPLUGINS.DEPENDENCIES) ;
 
  # Install targets
  Depends install_staticplugins : 
    [ DoInstall [ ConstructLibraryTarget $(name) ] : $(libdir) ] ;
  Depends install_staticplugins : $(STATICPLUGINS.DEPENDENCIES) ;
  Depends install_staticplugins : 
    [ DoInstall $(STATICPLUGINS.DEPENDENCIES) : $(libdir) ] ;
  Depends install_staticplugins : install_libs ;
    
  INSTALLEDLIBS_OPTIONAL += $(name) ;
  
  # Collect library dependencies. The idea is to built all libs needed by the
  # static plugins when "jam staticplugins" is invoked.
  for t in $(targets)
  {
    local deplibs ;
    if $($(t).NEEDLIBS)
    {
      deplibs += $($(t).NEEDLIBS) ;
    }
    deplibs = [ RemoveDups $(deplibs) ] ;
    Depends staticplugins : $(deplibs) ;
  }  
  Help staticplugins : 
    "Build the library with static versions of all plugins" ;
  Depends staticplugins : libs $(name) ;
}

#----------------------------------------------------------------------------

# Write out CFLAGS, LFLAGS needed by a plugin
rule WriteDependencies
{
  local cflags ;
  local lflags ;
  local depfile = $(<) ;
  local plugin = $(>) ;
  local name = $(3) ;
  local depfile_gristed = $(depfile:G=$(plugin)) ;
  MakeLocate $(depfile_gristed) : [ on $(depfile) GetVar LOCATE ] ;
  
  # Collect flags
  lflags += -l$(name) ;
  local libs = [ ResolveLibs $($(plugin).NEEDLIBS) ] ;
  lflags += -l$(libs) ;
  
  local l ;
  for l in $($(plugin).EXTERNALLIBS)
  {
    cflags += $($(l).CFLAGS) ;
    lflags += [ Filter $($(l).LFLAGS) : $(LINKLIBS) $(PLUGIN.LFLAGS) ] ;
  }
  lflags += [ Filter $($(plugin).LFLAGS) : $(LINKLIBS) $(PLUGIN.LFLAGS) ] ;
  CFLAGS on $(depfile_gristed) = "$(cflags)" ;
  LFLAGS on $(depfile_gristed) = "$(lflags)" ;
  
  if $($(depfile).FIRSTTIME) != "yes"
  {
    WriteDepFlags1 $(depfile_gristed) : $(plugin) ;
    $(depfile).FIRSTTIME = "yes" ;
  }
  else
  {
    WriteDepFlags2 $(depfile_gristed) : $(plugin) ;
  }
  Depends $(depfile) : $(depfile_gristed) ;
}

actions WriteDepFlags1 
{
  cat << EOT > $(<)
STATICPLUGINS.AVAILABLE += $(>) ;
STATICPLUGIN.$(>).CFLAGS = $(CFLAGS) ;
STATICPLUGIN.$(>).LFLAGS = $(LFLAGS) ;
EOT
}

actions WriteDepFlags2 
{
  cat << EOT >> $(<)
STATICPLUGINS.AVAILABLE += $(>) ;
STATICPLUGIN.$(>).CFLAGS = $(CFLAGS) ;
STATICPLUGIN.$(>).LFLAGS = $(LFLAGS) ;
EOT
}

actions piecemeal GenerateStaticPluginRegistration1
{
  echo 'namespace csStaticPluginInit' >> $(<)
  echo '{' >> $(<)
  for x in $(>)
  do
    n=`basename $x $(SUFMETA)`
    echo "static char const metainfo_${n}[] =" >> $(<)
    $(SED) 's:\\:\\\\:g;s:":\\":g;s:\(.*\):"\1":' < $x >>$(<)
    echo ";" >> $(<)
    $(SED) '/<implementation>/!d;s:[ 	]*<implementation>\(..*\)</implementation>:  #ifndef \1_FACTORY_REGISTER_DEFINED \
  #define \1_FACTORY_REGISTER_DEFINED \
    SCF_DEFINE_FACTORY_FUNC_REGISTRATION(\1) \
  #endif:g' < $x >> $(<)
  done
  echo '' >> $(<)
  for x in $(>)
  do
    n=`basename $x $(SUFMETA)`
    echo "class ${n}" >> $(<)
    echo "{" >> $(<)
    echo "SCF_REGISTER_STATIC_LIBRARY($n,metainfo_${n})" >>$(<)
    $(SED) '/<implementation>/!d;s:[ 	]*<implementation>\(..*\)</implementation>:  #ifndef \1_FACTORY_REGISTERED \
  #define \1_FACTORY_REGISTERED \
    \1_StaticInit \1_static_init__; \
  #endif:g' < $x >> $(<)
    echo "public:" >> $(<)
    echo " ${n}();" >> $(<)
    echo "};" >> $(<)
    echo "$n::$n() {}" >> $(<)
    echo "" >> $(<)
  done
  echo '}' >> $(<)
}
