#============================================================================
# Rules for library creation
# 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.
#
#============================================================================

# Suppress ar's noisy report that it created the archive we asked it to create.
if $(AR) && $(AR[2]) = ru { AR = $(AR[1]) ruc ; }

##  Library libname : sources [ : options ]
##    Build a library out of sourcefiles. All sourcefiles will be passed
##    to the Objects rule which tries to compile them into object-files. You
##    can create rules for your own filetypes with the UserObject rule. Header
##    files will just be ignored. They are only used for MSVC projectfile
##    generation.
##    Available options are 'shared' if you want to build a shared library on
##    platforms which support that. You can specify the 'noinstall' option if
##    you don't want an install target generated.
##    Don't specify any extensions for the library name, also leave out the
##    leading "lib".
##    Options:
##	noinstall: Do not set up a default installation target.
##	independent: The target will not be made a dependency of the libs and
##		     all targets.
##	shared: Create as a shared library on supported platforms.
##	nohelp: Do not invoke Help for this target.
##      notest: Do not set up unit-testing support for this target.
##	optional: Affects handling of the library in cs-config; it is only
##		  reported as available when actually built.
rule Library
{
  local options = $(3) ;
  CheckOptions noinstall independent shared nohelp notest optional : $(options) : $(<) ;

  local target = [ ConstructLibraryTarget $(<) : $(options) ] ;
  local sources = [ DoSourceGrist $(>) ] ;
  local objects ;
  local i ;
  for i in $(sources)
  {
    if $(i:S) = $(SUFOBJ)
    {
      objects += $(i) ;
    }
    else
    {
      objects += [ CompileObjects $(i) ] ;
    }
  }

  $(<)_TYPE = library ;
  $(<)_OBJECTS = $(objects) ;
  $(<)_SOURCES = $(sources) ;
  $(<)_TARGET = $(target) ;

  # so 'jam foo' works when it's really foo.exe (Windows) or foo.app (MacOS/X)
  if $(target) != $(<)
  {
    Depends $(<) : $(target) ;
    NotFile $(<) ;
  }

  # library depends on its member objects
  if ! [ IsElem independent : $(options) ]
  {
    if $(KEEPOBJS)
    {
      Depends obj : $(objects) ;
    }
    else
    {
      Depends libs : $(<) ;
    }
  }

  if ( ! [ IsElem shared : $(options) ] ) || ( $(BUILD_SHARED_LIBS) != "yes" )
  {
    $(<)_SHARED = "" ;
    LibraryStatic $(<) : $(objects) : [ Filter $(options) : shared ] :
      $(target) ;
  }
  else
  {
    $(<)_SHARED = "shared" ;
    LibraryShared $(<) : $(objects) : $(options) : $(target) ;
  }
  CFlags $(<) : $(LIBRARY.CFLAGS) ;
  LFlags $(<) : $(LIBRARY.LFLAGS) ;

  # create target clean rule
  Always $(<)clean ;
  NotFile $(<)clean ;
  Clean $(<)clean : $(objects) $($(<)_CLEANEXTRA) ;
  Clean clean : $($(<)_CLEANEXTRA) ;

  if ! [ IsElem nohelp : $(options) ]
  {
    local desc = [ Description $(<) ] ;
    if ! $(desc) { desc = "$(<) library" ; }
    Help $(<) : "Build the $(desc)" ;
  }

  if ! [ IsElem notest : $(options) ]
  {
    UnitTest $(<) ;
  }
}

##  LibDepends libname : dependant libraries
##    Make Library dependant on other libraries.  This will tell the build
##    system that your library uses functions from other libraries in the
##    project.  Note that a library shouldn't be linked with any external
##    library that should be done by the final application which uses the
##    library.
rule LibDepends
{
  $(<)_depends += $(>) ;
  
  if "$($(<)_SHARED)"
  {
    LinkWith $(<) : $(>) ;
  }

  UnitTestLibDepends $(<) : $(>) ;
}

#----------------------------------------------------------------------------
# private part

# LibraryStatic libname : sources : options : decoratedtarget
rule LibraryStatic
{
  local objects = $(>) ;
  local options = $(3) ;
  local target = $(4) ;

  # Set LOCATE for the library and its contents.  The bound
  # value shows up as $(NEEDLIBS) on the Link actions.
  # For compatibility, we only do this if the library doesn't
  # already have a path.
  if ! $(target:D)
  {
    MakeLocate $(target) $(target)($(objects:BS)) : $(LOCATE.OBJECTS)/libs ;
  }

  if $(NOARSCAN)
  {
    # If we can't scan the library to timestamp its contents,
    # we have to just make the library depend directly on the
    # on-disk object files.  
    Depends $(target) : $(objects) ;
  }
  else
  {
    # If we can scan the library, we make the library depend
    # on its members and each member depend on the on-disk
    # object file.
    Depends $(target) : $(target)($(objects:BS)) ;

    local i ;
    for i in $(objects)
    {
      Depends $(target)($(i:BS)) : $(i) ;
    }
  }

  # Generate install rules
  if ! [ IsElem noinstall : $(options) ]
  {
    if "$(RANLIB)"
    {
      Depends install_lib : [ DoInstall $(target) : $(libdir) : : Ranlib ] ;
    }
    else
    {
      Depends install_lib : [ DoInstall $(target) : $(libdir)  ] ;
    }
    
    # Add to global library list
    if [ IsElem optional : $(options) ]
    {
      INSTALLEDLIBS_OPTIONAL += $(<) ;
    }
    else
    {
      INSTALLEDLIBS += $(<) ;
    }
  }

  if $(CRELIB)
  {
    CreLib $(target) : $(objects[1]) ;
  }

  Archive $(target) : $(objects) ;

  if $(RANLIB)
  {
    Ranlib $(target) ;
  }

  # If we can't scan the library, we have to leave the .o's around.
  if ! ( $(NOARSCAN) || $(NOARUPDATE) || $(KEEPOBJS) )
  {
    RmTemps $(target) : $(objects) ;
  }
}

# LibraryStatic libname : sources : options : decoratedtarget
rule LibraryShared
{
  local objects = $(>) ;
  local options = $(3) ;
  local target = $(4) ;
  local linklib = [ ConstructSharedLibraryLinkLib $(<) : $(options) ] ;
  local deplibs ;

  local i ;
  for i in $(LIBDEPENDS)
  {
    deplibs += [ ConstructLibraryLinkTarget $(i) : $(options) ] ;
  }
  
  # Generate install rules
  if ! [ IsElem noinstall : $(options) ]
  {
    if $(TARGET.OS) != WIN32
    {
      Depends install_lib : [ DoInstall $(target) : $(libdir)  ] ;
    }
    else
    {
      Depends install_lib : [ DoInstall $(target) : $(bindir)  ] ;
      Depends install_lib : [ DoInstall $(linklib) : $(libdir)  ] ;
    }
    
    # Add to global library list
    if [ IsElem optional : $(options) ]
    {
      INSTALLEDLIBS_OPTIONAL += $(<) ;
    }
    else
    {
      INSTALLEDLIBS += $(<) ;
    }
  }

  Depends $(<) : $(linklib) ;
  if $(linklib) != $(target)
  {
    Depends $(linklib) : $(target) ;
    MakeLocate $(linklib) : $(LOCATE.OBJECTS)/libs ;
    SEARCH on $(linklib) = $(LOCATE.OBJECTS)/libs ;
  }
  Depends $(target) : $(objects) ;
  $(<)_CLEANEXTRA += $(target) ;
  Depends $(target) : $(deplibs) ;
    
  CFlags $(<) : [ FDefines CS_$(<:U)_LIB  ] ;
  LFlags $(<) : $(LINKLIBS) ;
    
  SystemLinkSharedLibrary $(target) : $(objects) $(deplibs) : $(linklib) ;
  
  if $(LINK.DEBUG.INFO.SEPARATE) = "yes"
  {
    local debugfile = [ SplitDebugInfo $(target) ] ;
    $(<)_CLEANEXTRA += $(debugfile) ;
    if ! [ IsElem noinstall : $(options) ]
    {
      NoCare $(debugfile) ;
      Depends install_lib : [ DoInstall $(debugfile) : $(libdir) : $(INSTALL_DATA) ] ;
    }
  }
}

rule ConstructLibraryTarget
{
  if ( ! [ IsElem shared : $(>) ] ) || ( $(BUILD_SHARED_LIBS) != "yes" )
  {
    return [ ConstructStaticLibraryTarget $(<) : [ Filter $(>) : shared ] ] ; 
  }
  else
  {
    return [ ConstructSharedLibraryTarget $(<) : $(>) ] ; 
  }
}

rule ConstructLibraryLinkTarget
{
  if ( ! [ IsElem shared : $(>) ] ) || ( $(BUILD_SHARED_LIBS) != "yes" )
  {
    return [ ConstructStaticLibraryTarget $(<) : [ Filter $(>) : shared ] ] ; 
  }
  else
  {
    return [ ConstructSharedLibraryLinkLib $(<) : $(>) ] ; 
  }
}

actions together Ranlib
{
  $(RANLIB) $(<)
}

# Construct pseudo target libs which is used instead of the pseudo target lib
# in Jambase
Depends lib : libs ;
NotFile libs ;
Help libs : "Build all link libraries" ;
